#include "sysconfig.h"

#if defined (JIT)

    #include "m68k.h"
    #include "newcpu.h"
    #include "comptbl.h"
    #include "compemu.h"
    #include "readcpu.h"

compop_func* compfunctbl[65536];
compop_func* nfcompfunctbl[65536];
    #ifdef NOFLAGS_SUPPORT
compop_func* nfcpufunctbl[65536];
    #endif
byte* comp_pc_p;

byte* start_pc_p;
uint start_pc;
uint current_block_pc_p;
uint current_block_start_target;
uint needed_flags;
static uint next_pc_p;
static uint taken_pc_p;
static int branch_cc;
int segvcount = 0;
int soft_flush_count = 0;
int hard_flush_count = 0;
int compile_count = 0;
int checksum_count = 0;
static byte* current_compile_p = nullptr;
static byte* max_compile_start;
byte* compiled_code = nullptr;
static int reg_alloc_run;

static int lazy_flush = 1;              // Flag: lazy translation cache invalidation
static int avoid_fpu = 1;               // Flag: compile FPU instructions ?
static int have_cmov = 0;               // target has CMOV instructions ?
static int have_rat_stall = 1;              // target has partial register stalls ?
const int tune_alignment = 1;               // Tune code alignments for running CPU ?
const int tune_nop_fillers = 1;             // Tune no-op fillers for architecture

static int setzflg_uses_bsf = 0;            // setzflg virtual instruction can use native BSF instruction correctly?
static int align_loops = 32;            // Align the start of loops
static int align_jumps = 32;            // Align the start of jumps

void* pushall_call_handler = nullptr;
static void* popall_do_nothing = nullptr;
static void* popall_exec_nostats = nullptr;
static void* popall_execute_normal = nullptr;
static void* popall_cache_miss = nullptr;
static void* popall_recompile_block = nullptr;
static void* popall_check_checksum = nullptr;

extern uint oink;
extern uint foink3;
extern uint foink;

/* The 68k only ever executes from even addresses. So right now, we
   waste half the entries in this array
   UPDATE: We now use those entries to store the start of the linked
   lists that we maintain for each hash result. */
cacheline cache_tags[TAGSIZE];
static int letit = 0;
static blockinfo* hold_bi[MAX_HOLD_BI];
static blockinfo* active;
static blockinfo* dormant;

op_properties prop[65536];

    #ifdef NOFLAGS_SUPPORT
/* 68040 */
extern const struct comptbl op_smalltbl_0_nf[];
    #endif
extern const struct comptbl op_smalltbl_0_comp_nf[];
extern const struct comptbl op_smalltbl_0_comp_ff[];
    #ifdef NOFLAGS_SUPPORT
/* 68020 + 68881 */
extern const struct cputbl op_smalltbl_1_nf[];
/* 68020 */
extern const struct cputbl op_smalltbl_2_nf[];
/* 68010 */
extern const struct cputbl op_smalltbl_3_nf[];
/* 68000 */
extern const struct cputbl op_smalltbl_4_nf[];
/* 68000 slow but compatible.  */
extern const struct cputbl op_smalltbl_5_nf[];
    #endif

static void flush_icache_hard(uint ptr, int n);

bigstate live;
static smallstate empty_ss;
static smallstate default_ss;
static int optlev;

static int writereg(int r, int size);
static void unlock(int r);
static void setlock(int r);
static int readreg_specific(int r, int size, int spec);
static int writereg_specific(int r, int size, int spec);
static void prepare_for_call_1();
static void prepare_for_call_2();
static void align_target(uint a);

static int nextused[VREGS];

static byte* popallspace;

uint m68k_pc_offset;

/* Some arithmetic operations can be optimized away if the operands
   are known to be constant. But that's only a good idea when the
   side effects they would have on the flags are not important. This
   variable indicates whether we need the side effects or not
 */
uint needflags = 0;

/* Flag handling is complicated.

   x86 instructions create flags, which quite often are exactly what we
   want. So at times, the "68k" flags are actually in the x86 flags.

   Then again, sometimes we do x86 instructions that clobber the x86
   flags, but don't represent a corresponding m68k instruction. In that
   case, we have to save them.

   We used to save them to the stack, but now store them back directly
   into the regflags.cznv of the traditional emulation. Thus some odd
   names.

   So flags can be in either of two places (used to be three; boy were
   things complicated back then!); And either place can contain either
   valid flags or invalid trash (and on the stack, there was also the
   option of "nothing at all", now gone). A couple of variables keep
   track of the respective states.

   To make things worse, we might or might not be interested in the flags.
   by default, we are, but a call to dont_care_flags can change that
   until the next call to live_flags. If we are not, pretty much whatever
   is in the register and/or the native flags is seen as valid.
 */

static __forceinline blockinfo* get_blockinfo(uint cl)
{
    return cache_tags[cl + 1].bi;
}

static __forceinline blockinfo* get_blockinfo_addr(void* addr)
{
    blockinfo* bi = get_blockinfo(cacheline(addr));

    while (bi)
    {
        if (bi->pc_p == addr)
            return bi;
        bi = bi->next_same_cl;
    }
    return nullptr;
}

/*******************************************************************
* All sorts of list related functions for all of the lists        *
*******************************************************************/

static __forceinline void remove_from_cl_list(blockinfo* bi)
{
    uint cl = cacheline(bi->pc_p);

    if (bi->prev_same_cl_p)
        *(bi->prev_same_cl_p) = bi->next_same_cl;
    if (bi->next_same_cl)
        bi->next_same_cl->prev_same_cl_p = bi->prev_same_cl_p;
    if (cache_tags[cl + 1].bi)
        cache_tags[cl].handler = cache_tags[cl + 1].bi->handler_to_use;
    else
        cache_tags[cl].handler = (cpuop_func*)popall_execute_normal;
}

static __forceinline void remove_from_list(blockinfo* bi)
{
    if (bi->prev_p)
        *(bi->prev_p) = bi->next;
    if (bi->next)
        bi->next->prev_p = bi->prev_p;
}

static __forceinline void remove_from_lists(blockinfo* bi)
{
    remove_from_list(bi);
    remove_from_cl_list(bi);
}

static __forceinline void add_to_cl_list(blockinfo* bi)
{
    uint cl = cacheline(bi->pc_p);

    if (cache_tags[cl + 1].bi)
        cache_tags[cl + 1].bi->prev_same_cl_p = &(bi->next_same_cl);
    bi->next_same_cl = cache_tags[cl + 1].bi;

    cache_tags[cl + 1].bi = bi;
    bi->prev_same_cl_p = &(cache_tags[cl + 1].bi);

    cache_tags[cl].handler = bi->handler_to_use;
}

static __forceinline void raise_in_cl_list(blockinfo* bi)
{
    remove_from_cl_list(bi);
    add_to_cl_list(bi);
}

static __forceinline void add_to_active(blockinfo* bi)
{
    if (active)
        active->prev_p = &(bi->next);
    bi->next = active;

    active = bi;
    bi->prev_p = &active;
}

static __forceinline void remove_dep(dependency* d)
{
    if (d->prev_p)
        *(d->prev_p) = d->next;
    if (d->next)
        d->next->prev_p = d->prev_p;
    d->prev_p = nullptr;
    d->next = nullptr;
}

/* This block's code is about to be thrown away, so it no longer
   depends on anything else */
static __forceinline void remove_deps(blockinfo* bi)
{
    remove_dep(&(bi->dep[0]));
    remove_dep(&(bi->dep[1]));
}

static __forceinline void adjust_jmpdep(dependency* d, void* a)
{
    *(d->jmp_off) = (uint)a - ((uint)d->jmp_off + 4);
}

/********************************************************************
* Soft flush handling support functions                            *
********************************************************************/

static __forceinline void set_dhtu(blockinfo* bi, void* dh)
{
    // Logger::Write (L"JIT: bi is %p\n",bi);
    if (dh != bi->direct_handler_to_use)
    {
        dependency* x = bi->deplist;
        // Logger::Write (L"JIT: bi->deplist=%p\n",bi->deplist);
        while (x)
        {
            // Logger::Write (L"JIT: x is %p\n",x);
            // Logger::Write (L"JIT: x->next is %p\n",x->next);
            // Logger::Write (L"JIT: x->prev_p is %p\n",x->prev_p);

            if (x->jmp_off)
            {
                adjust_jmpdep(x, dh);
            }
            x = x->next;
        }
        bi->direct_handler_to_use = (cpuop_func*)dh;
    }
}

static __forceinline void invalidate_block(blockinfo* bi)
{
    int i;

    bi->optlevel = 0;
    bi->count = g_curr_conf.jit_optcount[0] - 1;
    bi->handler = nullptr;
    bi->handler_to_use = (cpuop_func*)popall_execute_normal;
    bi->direct_handler = nullptr;
    set_dhtu(bi, bi->direct_pen);
    bi->needed_flags = 0xff;

    for (i = 0; i < 2; i++)
    {
        bi->dep[i].jmp_off = nullptr;
        bi->dep[i].target = nullptr;
    }
    remove_deps(bi);
}

static __forceinline void create_jmpdep(blockinfo* bi, int i, uint* jmpaddr, uint target)
{
    blockinfo* tbi = get_blockinfo_addr((void*)target);

    Dif(!tbi)
    {
        Logger::JitAbort(L"JIT: Could not create jmpdep!\n");
    }
    bi->dep[i].jmp_off = jmpaddr;
    bi->dep[i].target = tbi;
    bi->dep[i].next = tbi->deplist;
    if (bi->dep[i].next)
        bi->dep[i].next->prev_p = &(bi->dep[i].next);
    bi->dep[i].prev_p = &(tbi->deplist);
    tbi->deplist = &(bi->dep[i]);
}

static __forceinline void big_to_small_state(bigstate* b, smallstate* s)
{
    int i;
    int count = 0;

    for (i = 0; i < N_REGS; i++)
    {
        s->nat[i].validsize = 0;
        s->nat[i].dirtysize = 0;
        if (b->nat[i].nholds)
        {
            int index = b->nat[i].nholds - 1;
            int r = b->nat[i].holds[index];
            s->nat[i].holds = r;
            s->nat[i].validsize = b->state[r].validsize;
            s->nat[i].dirtysize = b->state[r].dirtysize;
            count++;
        }
    }
    Logger::Write(L"JIT: count=%d\n", count);
    for (i = 0; i < N_REGS; i++)    // FIXME --- don't do dirty yet
    {
        s->nat[i].dirtysize = 0;
    }
}

static __forceinline void attached_state(blockinfo* bi)
{
    bi->havestate = 1;
    if (bi->direct_handler_to_use == bi->direct_handler)
        set_dhtu(bi, bi->direct_pen);
    bi->direct_handler = bi->direct_pen;
    bi->status = BI_TARGETTED;
}

static __forceinline blockinfo* get_blockinfo_addr_new(void* addr, int setstate)
{
    blockinfo* bi = get_blockinfo_addr(addr);
    int i;

    #if USE_OPTIMIZER
    if (reg_alloc_run)
        return nullptr;
    #endif
    if (!bi)
    {
        for (i = 0; i < MAX_HOLD_BI && !bi; i++)
        {
            if (hold_bi[i])
            {
                // uint cl = cacheline(addr);

                bi = hold_bi[i];
                hold_bi[i] = nullptr;
                bi->pc_p = (byte*)addr;
                invalidate_block(bi);
                add_to_active(bi);
                add_to_cl_list(bi);
            }
        }
    }
    if (!bi)
    {
        Logger::JitAbort(L"JIT: Looking for blockinfo, can't find free one\n");
    }

    #if JIT_DEBUG_DEF > 1
    if (setstate &&
        !bi->havestate)
    {
        big_to_small_state(&live, &(bi->env));
        attached_state(bi);
    }
    #endif
    return bi;
}

static void prepare_block(blockinfo* bi);

static __forceinline void alloc_blockinfos()
{
    int i;
    blockinfo* bi;

    for (i = 0; i < MAX_HOLD_BI; i++)
    {
        if (hold_bi[i])
            return;
        bi = hold_bi[i] = (blockinfo*)current_compile_p;
        current_compile_p += sizeof(blockinfo);

        prepare_block(bi);
    }
}

/********************************************************************
* Preferences handling. This is just a convenient place to put it  *
********************************************************************/
extern bool have_done_picasso;

bool check_prefs_changed_comp()
{
    bool changed = 0;
    static int cachesize_prev;

    if (g_curr_conf.jit_no_flags != g_changed_conf.jit_no_flags ||
        g_curr_conf.jit_hard_flush != g_changed_conf.jit_hard_flush ||
        g_curr_conf.jit_const_jump != g_changed_conf.jit_const_jump ||
        g_curr_conf.jit_fpu != g_changed_conf.jit_fpu ||
        g_curr_conf.jit_fpu_more_compatible != g_changed_conf.jit_fpu_more_compatible)
        changed = 1;

    g_curr_conf.jit_no_flags = g_changed_conf.jit_no_flags;
    g_curr_conf.jit_hard_flush = g_changed_conf.jit_hard_flush;
    g_curr_conf.jit_const_jump = g_changed_conf.jit_const_jump;
    g_curr_conf.jit_fpu = g_changed_conf.jit_fpu;
    g_curr_conf.jit_fpu_more_compatible = g_changed_conf.jit_fpu_more_compatible;

    if (g_curr_conf.jit_cache_size != g_changed_conf.jit_cache_size)
    {
        if (g_curr_conf.jit_cache_size && !g_changed_conf.jit_cache_size)
        {
            cachesize_prev = g_curr_conf.jit_cache_size;
        }
        g_curr_conf.jit_cache_size = g_changed_conf.jit_cache_size;
        alloc_cache();
        changed = 1;
    }

    if (changed)
    {
        if (Logging::JIT_DEBUG)
        {
            Logger::Write(L"JIT: cache=%d. fpu=%d nf=%d const=%d hard=%d\n",
                g_curr_conf.jit_cache_size, g_curr_conf.jit_fpu, g_curr_conf.jit_no_flags, g_curr_conf.jit_const_jump, g_curr_conf.jit_hard_flush);
        }
    }
    return changed;
}

/********************************************************************
* Get the optimizer stuff                                          *
********************************************************************/

// #include "compemu_optimizer.c"
    #include "compemu_optimizer_x86.cpp"

/********************************************************************
* Functions to emit data into memory, and other general support    *
********************************************************************/

static byte* target;

static void emit_init()
{
}

static __forceinline void emit_byte(byte x)
{
    *target++ = x;
}

static __forceinline void emit_word(ushort x)
{
    *((ushort*)target) = x;
    target += 2;
}

static __forceinline void emit_long(uint x)
{
    *((uint*)target) = x;
    target += 4;
}

static __forceinline uint reverse32(uint oldv)
{
    return ((oldv >> 24) & 0xff) | ((oldv >> 8) & 0xff00) |
           ((oldv << 8) & 0xff0000) | ((oldv << 24) & 0xff000000);
}

void set_target(byte* t)
{
    lopt_emit_all();
    target = t;
}

static __forceinline byte* get_target_noopt()
{
    return target;
}

static __forceinline byte* get_target()
{
    lopt_emit_all();
    return get_target_noopt();
}

/********************************************************************
* Getting the information about the target CPU                     *
********************************************************************/

    #include "compemu_raw_x86.cpp"

/********************************************************************
* Flags status handling. EMIT TIME!                                *
********************************************************************/

static void bt_l_ri_noclobber(R4 r, IMM i);

static void make_flags_live_internal()
{
    if (live.flags_in_flags == VALID)
        return;
    Dif(live.flags_on_stack == TRASH)
    {
        Logger::JitAbort(L"JIT: Want flags, got something on stack, but it is TRASH\n");
    }
    if (live.flags_on_stack == VALID)
    {
        int tmp;
        tmp = readreg_specific(FLAGTMP, 4, FLAG_NREG2);
        raw_reg_to_flags(tmp);
        unlock(tmp);

        live.flags_in_flags = VALID;
        return;
    }
    Logger::JitAbort(L"JIT: Huh? live.flags_in_flags=%d, live.flags_on_stack=%d, but need to make live\n",
        live.flags_in_flags, live.flags_on_stack);
}

static void flags_to_stack()
{
    if (live.flags_on_stack == VALID)
        return;
    if (!live.flags_are_important)
    {
        live.flags_on_stack = VALID;
        return;
    }
    Dif(live.flags_in_flags != VALID)
    Logger::JitAbort(L"flags_to_stack != VALID");
    else
    {
        int tmp;
        tmp = writereg_specific(FLAGTMP, 4, FLAG_NREG1);
        raw_flags_to_reg(tmp);
        unlock(tmp);
    }
    live.flags_on_stack = VALID;
}

static __forceinline void clobber_flags()
{
    if (live.flags_in_flags == VALID && live.flags_on_stack != VALID)
        flags_to_stack();
    live.flags_in_flags = TRASH;
}

/* Prepare for leaving the compiled stuff */
static __forceinline void flush_flags()
{
    flags_to_stack();
    return;
}

int touchcnt;

/********************************************************************
* register allocation per block logging                            *
********************************************************************/

static sbyte vstate[VREGS];
static sbyte nstate[N_REGS];

    #define L_UNKNOWN -127
    #define L_UNAVAIL -1
    #define L_NEEDED -2
    #define L_UNNEEDED -3

static __forceinline void log_startblock()
{
    int i;
    for (i = 0; i < VREGS; i++)
        vstate[i] = L_UNKNOWN;
    for (i = 0; i < N_REGS; i++)
        nstate[i] = L_UNKNOWN;
}

static __forceinline void log_isused(int n)
{
    if (nstate[n] == L_UNKNOWN)
        nstate[n] = L_UNAVAIL;
}

static __forceinline void log_isreg(int n, int r)
{
    if (nstate[n] == L_UNKNOWN)
        nstate[n] = r;
    if (vstate[r] == L_UNKNOWN)
        vstate[r] = L_NEEDED;
}

static __forceinline void log_clobberreg(int r)
{
    if (vstate[r] == L_UNKNOWN)
        vstate[r] = L_UNNEEDED;
}

/* This ends all possibility of clever register allocation */

static __forceinline void log_flush()
{
    int i;
    for (i = 0; i < VREGS; i++)
        if (vstate[i] == L_UNKNOWN)
            vstate[i] = L_NEEDED;
    for (i = 0; i < N_REGS; i++)
        if (nstate[i] == L_UNKNOWN)
            nstate[i] = L_UNAVAIL;
}

static __forceinline void log_dump()
{
    // int i;

    // return;

    /*Logger::Write(L"----------------------\n");
       for (i = 0; i < N_REGS; i++)
       {
        switch (nstate[i])
        {
            case L_UNKNOWN: Logger::Write(L"Nat %d : UNKNOWN\n", i); break;
            case L_UNAVAIL: Logger::Write(L"Nat %d : UNAVAIL\n", i); break;
            default:        Logger::Write(L"Nat %d : %d\n", i, nstate[i]); break;
        }
       }
       for (i = 0; i < VREGS; i++)
       {
        if (vstate[i] == L_UNNEEDED)
            Logger::Write(L"Virt %d: UNNEEDED\n", i);
       }*/
}

/********************************************************************
* register status handling. EMIT TIME!                             *
********************************************************************/

static __forceinline void set_status(int r, int status)
{
    if (status == ISCONST)
        log_clobberreg(r);
    live.state[r].status = status;
}

static __forceinline int isinreg(int r)
{
    return live.state[r].status == CLEAN || live.state[r].status == DIRTY;
}

static __forceinline void adjust_nreg(int r, uint val)
{
    if (!val)
        return;
    raw_lea_l_brr(r, r, val);
}

static void tomem(int r)
{
    int rr = live.state[r].realreg;

    if (isinreg(r))
    {
        if (live.state[r].val &&
            live.nat[rr].nholds == 1 &&
            !live.nat[rr].locked)
        {
            //  Logger::Write (L"JIT: RemovingA offset %x from reg %d (%d) at %p\n",
            //    live.state[r].val,r,rr,target);
            adjust_nreg(rr, live.state[r].val);
            live.state[r].val = 0;
            live.state[r].dirtysize = 4;
            set_status(r, DIRTY);
        }
    }

    if (live.state[r].status == DIRTY)
    {
        switch (live.state[r].dirtysize)
        {
            case 1: raw_mov_b_mr((uint)live.state[r].mem, rr);
                break;
            case 2: raw_mov_w_mr((uint)live.state[r].mem, rr);
                break;
            case 4: raw_mov_l_mr((uint)live.state[r].mem, rr);
                break;
            default: abort();
        }
        set_status(r, CLEAN);
        live.state[r].dirtysize = 0;
    }
}

static __forceinline int isconst(int r)
{
    return live.state[r].status == ISCONST;
}

int is_const(int r)
{
    return isconst(r);
}

static __forceinline void writeback_const(int r)
{
    if (!isconst(r))
        return;
    Dif(live.state[r].needflush == NF_HANDLER)
    {
        Logger::JitAbort(L"JIT: Trying to write back constant NF_HANDLER!\n");
    }

    raw_mov_l_mi((uint)live.state[r].mem, live.state[r].val);
    live.state[r].val = 0;
    set_status(r, INMEM);
}

static __forceinline void tomem_c(int r)
{
    if (isconst(r))
    {
        writeback_const(r);
    }
    else
        tomem(r);
}

static void evict(int r)
{
    int rr;

    if (!isinreg(r))
        return;
    tomem(r);
    rr = live.state[r].realreg;

    Dif(live.nat[rr].locked &&
        live.nat[rr].nholds == 1)
    {
        Logger::JitAbort(L"JIT: register %d in nreg %d is locked!\n", r, live.state[r].realreg);
    }

    live.nat[rr].nholds--;
    if (live.nat[rr].nholds != live.state[r].realind)   /* Was not last */
    {
        int topreg = live.nat[rr].holds[live.nat[rr].nholds];
        int thisind = live.state[r].realind;
        live.nat[rr].holds[thisind] = topreg;
        live.state[topreg].realind = thisind;
    }
    live.state[r].realreg = -1;
    set_status(r, INMEM);
}

static __forceinline void free_nreg(int r)
{
    int i = live.nat[r].nholds;

    while (i)
    {
        int vr;

        --i;
        vr = live.nat[r].holds[i];
        evict(vr);
    }
    Dif(live.nat[r].nholds != 0)
    {
        Logger::JitAbort(L"JIT: Failed to free nreg %d, nholds is %d\n", r, live.nat[r].nholds);
    }
}

/* Use with care! */
static __forceinline void isclean(int r)
{
    if (!isinreg(r))
        return;
    live.state[r].validsize = 4;
    live.state[r].dirtysize = 0;
    live.state[r].val = 0;
    set_status(r, CLEAN);
}

static __forceinline void disassociate(int r)
{
    isclean(r);
    evict(r);
}

static __forceinline void set_const(int r, uint val)
{
    disassociate(r);
    live.state[r].val = val;
    set_status(r, ISCONST);
}

static __forceinline uint get_offset(int r)
{
    return live.state[r].val;
}

static int alloc_reg_hinted(int r, int size, int willclobber, int hint)
{
    int bestreg;
    int when;
    int i;
    int badness = 0; /* to shut up gcc */
    bestreg = -1;
    when = 2000000000;

    for (i = N_REGS; i--;)
    {
        badness = live.nat[i].touched;
        if (live.nat[i].nholds == 0)
            badness = 0;
        if (i == hint)
            badness -= 200000000;
        if (!live.nat[i].locked && badness < when)
        {
            if ((size == 1 && live.nat[i].canbyte) ||
                (size == 2 && live.nat[i].canword) ||
                (size == 4))
            {
                bestreg = i;
                when = badness;
                if (live.nat[i].nholds == 0 && hint < 0)
                    break;
                if (i == hint)
                    break;
            }
        }
    }
    Dif(bestreg == -1)
    Logger::JitAbort(L"alloc_reg_hinted bestreg=-1");

    if (live.nat[bestreg].nholds > 0)
    {
        free_nreg(bestreg);
    }
    if (isinreg(r))
    {
        int rr = live.state[r].realreg;
        /* This will happen if we read a partially dirty register at a
           bigger size */
        Dif(willclobber || live.state[r].validsize >= size)
        Logger::JitAbort(L"willclobber || live.state[r].validsize>=size");
        Dif(live.nat[rr].nholds != 1)
        Logger::JitAbort(L"live.nat[rr].nholds!=1");
        if (size == 4 && live.state[r].validsize == 2)
        {
            log_isused(bestreg);
            raw_mov_l_rm(bestreg, (uint)live.state[r].mem);
            raw_bswap_32(bestreg);
            raw_zero_extend_16_rr(rr, rr);
            raw_zero_extend_16_rr(bestreg, bestreg);
            raw_bswap_32(bestreg);
            raw_lea_l_rr_indexed(rr, rr, bestreg);
            live.state[r].validsize = 4;
            live.nat[rr].touched = touchcnt++;
            return rr;
        }
        if (live.state[r].validsize == 1)
        {
            /* Nothing yet */
        }
        evict(r);
    }

    if (!willclobber)
    {
        if (live.state[r].status != UNDEF)
        {
            if (isconst(r))
            {
                raw_mov_l_ri(bestreg, live.state[r].val);
                live.state[r].val = 0;
                live.state[r].dirtysize = 4;
                set_status(r, DIRTY);
                log_isused(bestreg);
            }
            else
            {
                if (r == FLAGTMP)
                    raw_load_flagreg(bestreg, r);
                else if (r == FLAGX)
                    raw_load_flagx(bestreg, r);
                else
                {
                    raw_mov_l_rm(bestreg, (uint)live.state[r].mem);
                }
                live.state[r].dirtysize = 0;
                set_status(r, CLEAN);
                log_isreg(bestreg, r);
            }
        }
        else
        {
            live.state[r].val = 0;
            live.state[r].dirtysize = 0;
            set_status(r, CLEAN);
            log_isused(bestreg);
        }
        live.state[r].validsize = 4;
    }
    else   /* this is the easiest way, but not optimal. FIXME! */
    {
        /* Now it's trickier, but hopefully still OK */
        if (!isconst(r) || size == 4)
        {
            live.state[r].validsize = size;
            live.state[r].dirtysize = size;
            live.state[r].val = 0;
            set_status(r, DIRTY);
            if (size == 4)
                log_isused(bestreg);
            else
                log_isreg(bestreg, r);
        }
        else
        {
            if (live.state[r].status != UNDEF)
                raw_mov_l_ri(bestreg, live.state[r].val);
            live.state[r].val = 0;
            live.state[r].validsize = 4;
            live.state[r].dirtysize = 4;
            set_status(r, DIRTY);
            log_isused(bestreg);
        }
    }
    live.state[r].realreg = bestreg;
    live.state[r].realind = live.nat[bestreg].nholds;
    live.nat[bestreg].touched = touchcnt++;
    live.nat[bestreg].holds[live.nat[bestreg].nholds] = r;
    live.nat[bestreg].nholds++;

    return bestreg;
}

static int alloc_reg(int r, int size, int willclobber)
{
    return alloc_reg_hinted(r, size, willclobber, -1);
}

static void unlock(int r)
{
    Dif(!live.nat[r].locked)
    Logger::JitAbort(L"unlock %d not locked", r);
    live.nat[r].locked--;
}

static void setlock(int r)
{
    live.nat[r].locked++;
}

static void mov_nregs(int d, int s)
{
    // int ns = live.nat[s].nholds;
    int nd = live.nat[d].nholds;
    int i;

    if (s == d)
        return;

    if (nd > 0)
        free_nreg(d);

    raw_mov_l_rr(d, s);
    log_isused(d);

    for (i = 0; i < live.nat[s].nholds; i++)
    {
        int vs = live.nat[s].holds[i];

        live.state[vs].realreg = d;
        live.state[vs].realind = i;
        live.nat[d].holds[i] = vs;
    }
    live.nat[d].nholds = live.nat[s].nholds;

    live.nat[s].nholds = 0;
}

static __forceinline void make_exclusive(int r, int size, int spec)
{
    reg_status oldstate;
    int rr = live.state[r].realreg;
    int nr;
    int nind;
    int ndirt = 0;
    int i;

    if (!isinreg(r))
        return;
    if (live.nat[rr].nholds == 1)
        return;
    for (i = 0; i < live.nat[rr].nholds; i++)
    {
        int vr = live.nat[rr].holds[i];
        if (vr != r &&
            (live.state[vr].status == DIRTY || live.state[vr].val))
            ndirt++;
    }
    if (!ndirt && size < live.state[r].validsize && !live.nat[rr].locked)
    {
        /* Everything else is clean, so let's keep this register */
        for (i = 0; i < live.nat[rr].nholds; i++)
        {
            int vr = live.nat[rr].holds[i];
            if (vr != r)
            {
                evict(vr);
                i--; /* Try that index again! */
            }
        }
        Dif(live.nat[rr].nholds != 1)
        {
            Logger::JitAbort(L"JIT: natreg %d holds %d vregs, %d not exclusive\n",
                rr, live.nat[rr].nholds, r);
        }
        return;
    }

    /* We have to split the register */
    oldstate = live.state[r];

    setlock(rr); /* Make sure this doesn't go away */
    /* Forget about r being in the register rr */
    disassociate(r);
    /* Get a new register, that we will clobber completely */
    if (oldstate.status == DIRTY)
    {
        /* If dirtysize is <4, we need a register that can handle the
           eventual smaller memory store! Thanks to Quake68k for exposing
           this detail ;-) */
        nr = alloc_reg_hinted(r, oldstate.dirtysize, 1, spec);
    }
    else
    {
        nr = alloc_reg_hinted(r, 4, 1, spec);
    }
    nind = live.state[r].realind;
    live.state[r] = oldstate;   /* Keep all the old state info */
    live.state[r].realreg = nr;
    live.state[r].realind = nind;

    if (size < live.state[r].validsize)
    {
        if (live.state[r].val)
        {
            /* Might as well compensate for the offset now */
            raw_lea_l_brr(nr, rr, oldstate.val);
            live.state[r].val = 0;
            live.state[r].dirtysize = 4;
            set_status(r, DIRTY);
        }
        else
            raw_mov_l_rr(nr, rr);  /* Make another copy */
    }
    unlock(rr);
}

static __forceinline void add_offset(int r, uint off)
{
    live.state[r].val += off;
}

static __forceinline void remove_offset(int r, int spec)
{
    int rr;

    if (isconst(r))
        return;
    if (live.state[r].val == 0)
        return;
    if (isinreg(r) && live.state[r].validsize < 4)
        evict(r);

    if (!isinreg(r))
        alloc_reg_hinted(r, 4, 0, spec);

    Dif(live.state[r].validsize != 4)
    {
        Logger::JitAbort(L"JIT: Validsize=%d in remove_offset\n", live.state[r].validsize);
    }
    make_exclusive(r, 0, -1);
    /* make_exclusive might have done the job already */
    if (live.state[r].val == 0)
        return;

    rr = live.state[r].realreg;

    if (live.nat[rr].nholds == 1)
    {
        // Logger::Write (L"JIT: RemovingB offset %x from reg %d (%d) at %p\n",
        //        live.state[r].val,r,rr,target);
        adjust_nreg(rr, live.state[r].val);
        live.state[r].dirtysize = 4;
        live.state[r].val = 0;
        set_status(r, DIRTY);
        return;
    }
    Logger::JitAbort(L"JIT: Failed in remove_offset\n");
}

static __forceinline void remove_all_offsets()
{
    int i;

    for (i = 0; i < VREGS; i++)
        remove_offset(i, -1);
}

static __forceinline int readreg_general(int r, int size, int spec, int can_offset)
{
    int n;
    int answer = -1;

    if (live.state[r].status == UNDEF)
    {
        Logger::Write(L"JIT: WARNING: Unexpected read of undefined register %d\n", r);
    }
    if (!can_offset)
        remove_offset(r, spec);

    if (isinreg(r) && live.state[r].validsize >= size)
    {
        n = live.state[r].realreg;
        switch (size)
        {
            case 1:
                if (live.nat[n].canbyte || spec >= 0)
                {
                    answer = n;
                }
                break;
            case 2:
                if (live.nat[n].canword || spec >= 0)
                {
                    answer = n;
                }
                break;
            case 4:
                answer = n;
                break;
            default: abort();
        }
        if (answer < 0)
            evict(r);
    }
    /* either the value was in memory to start with, or it was evicted and
       is in memory now */
    if (answer < 0)
    {
        answer = alloc_reg_hinted(r, spec >= 0 ? 4 : size, 0, spec);
    }

    if (spec >= 0 && spec != answer)
    {
        /* Too bad */
        mov_nregs(spec, answer);
        answer = spec;
    }
    live.nat[answer].locked++;
    live.nat[answer].touched = touchcnt++;
    return answer;
}

static int readreg(int r, int size)
{
    return readreg_general(r, size, -1, 0);
}

static int readreg_specific(int r, int size, int spec)
{
    return readreg_general(r, size, spec, 0);
}

static int readreg_offset(int r, int size)
{
    return readreg_general(r, size, -1, 1);
}

static __forceinline int writereg_general(int r, int size, int spec)
{
    int n;
    int answer = -1;

    if (size < 4)
    {
        remove_offset(r, spec);
    }

    make_exclusive(r, size, spec);
    if (isinreg(r))
    {
        int nvsize = size > live.state[r].validsize ? size : live.state[r].validsize;
        int ndsize = size > live.state[r].dirtysize ? size : live.state[r].dirtysize;
        n = live.state[r].realreg;

        Dif(live.nat[n].nholds != 1)
        Logger::JitAbort(L"live.nat[%d].nholds!=1", n);
        switch (size)
        {
            case 1:
                if (live.nat[n].canbyte || spec >= 0)
                {
                    live.state[r].dirtysize = ndsize;
                    live.state[r].validsize = nvsize;
                    answer = n;
                }
                break;
            case 2:
                if (live.nat[n].canword || spec >= 0)
                {
                    live.state[r].dirtysize = ndsize;
                    live.state[r].validsize = nvsize;
                    answer = n;
                }
                break;
            case 4:
                live.state[r].dirtysize = ndsize;
                live.state[r].validsize = nvsize;
                answer = n;
                break;
            default: abort();
        }
        if (answer < 0)
            evict(r);
    }
    /* either the value was in memory to start with, or it was evicted and
       is in memory now */
    if (answer < 0)
    {
        answer = alloc_reg_hinted(r, size, 1, spec);
    }
    if (spec >= 0 && spec != answer)
    {
        mov_nregs(spec, answer);
        answer = spec;
    }
    if (live.state[r].status == UNDEF)
        live.state[r].validsize = 4;
    live.state[r].dirtysize = size > live.state[r].dirtysize ? size : live.state[r].dirtysize;
    live.state[r].validsize = size > live.state[r].validsize ? size : live.state[r].validsize;

    live.nat[answer].locked++;
    live.nat[answer].touched = touchcnt++;
    if (size == 4)
    {
        live.state[r].val = 0;
    }
    else
    {
        Dif(live.state[r].val)
        {
            Logger::JitAbort(L"JIT: Problem with val\n");
        }
    }
    set_status(r, DIRTY);
    return answer;
}

static int writereg(int r, int size)
{
    return writereg_general(r, size, -1);
}

static int writereg_specific(int r, int size, int spec)
{
    return writereg_general(r, size, spec);
}

static __forceinline int rmw_general(int r, int wsize, int rsize, int spec)
{
    int n;
    int answer = -1;

    if (live.state[r].status == UNDEF)
    {
        Logger::Write(L"JIT: WARNING: Unexpected read of undefined register %d\n", r);
    }
    remove_offset(r, spec);
    make_exclusive(r, 0, spec);

    Dif(wsize < rsize)
    {
        Logger::JitAbort(L"JIT: Cannot handle wsize<rsize in rmw_general()\n");
    }
    if (isinreg(r) && live.state[r].validsize >= rsize)
    {
        n = live.state[r].realreg;
        Dif(live.nat[n].nholds != 1)
        Logger::JitAbort(L"live.nat[n].nholds!=1", n);

        switch (rsize)
        {
            case 1:
                if (live.nat[n].canbyte || spec >= 0)
                {
                    answer = n;
                }
                break;
            case 2:
                if (live.nat[n].canword || spec >= 0)
                {
                    answer = n;
                }
                break;
            case 4:
                answer = n;
                break;
            default: abort();
        }
        if (answer < 0)
            evict(r);
    }
    /* either the value was in memory to start with, or it was evicted and
       is in memory now */
    if (answer < 0)
    {
        answer = alloc_reg_hinted(r, spec >= 0 ? 4 : rsize, 0, spec);
    }

    if (spec >= 0 && spec != answer)
    {
        /* Too bad */
        mov_nregs(spec, answer);
        answer = spec;
    }
    if (wsize > live.state[r].dirtysize)
        live.state[r].dirtysize = wsize;
    if (wsize > live.state[r].validsize)
        live.state[r].validsize = wsize;
    set_status(r, DIRTY);

    live.nat[answer].locked++;
    live.nat[answer].touched = touchcnt++;

    Dif(live.state[r].val)
    {
        Logger::JitAbort(L"JIT: Problem with val(rmw)\n");
    }
    return answer;
}

static int rmw(int r, int wsize, int rsize)
{
    return rmw_general(r, wsize, rsize, -1);
}

static int rmw_specific(int r, int wsize, int rsize, int spec)
{
    return rmw_general(r, wsize, rsize, spec);
}

/* needed for restoring the carry flag on non-P6 cores */
static void bt_l_ri_noclobber(R4 r, IMM i)
{
    int size = 4;
    if (i < 16)
        size = 2;
    r = readreg(r, size);
    raw_bt_l_ri(r, i);
    unlock(r);
}

/********************************************************************
* FPU register status handling. EMIT TIME!                         *
********************************************************************/

static void f_tomem(int r)
{
    if (live.fate[r].status == DIRTY)
    {
        #if USE_LONG_DOUBLE
        raw_fmov_ext_mr((uint)live.fate[r].mem, live.fate[r].realreg);
        #else
        raw_fmov_mr((uint)live.fate[r].mem, live.fate[r].realreg);
        #endif
        live.fate[r].status = CLEAN;
    }
}

static void f_tomem_drop(int r)
{
    if (live.fate[r].status == DIRTY)
    {
        #if USE_LONG_DOUBLE
        raw_fmov_ext_mr_drop((uint)live.fate[r].mem, live.fate[r].realreg);
        #else
        raw_fmov_mr_drop((uint)live.fate[r].mem, live.fate[r].realreg);
        #endif
        live.fate[r].status = INMEM;
    }
}

static __forceinline int f_isinreg(int r)
{
    return live.fate[r].status == CLEAN || live.fate[r].status == DIRTY;
}

static void f_evict(int r)
{
    int rr;

    if (!f_isinreg(r))
        return;
    rr = live.fate[r].realreg;
    if (live.fat[rr].nholds == 1)
        f_tomem_drop(r);
    else
        f_tomem(r);

    Dif(live.fat[rr].locked &&
        live.fat[rr].nholds == 1)
    {
        Logger::JitAbort(L"JIT: FPU register %d in nreg %d is locked!\n", r, live.fate[r].realreg);
    }

    live.fat[rr].nholds--;
    if (live.fat[rr].nholds != live.fate[r].realind)   /* Was not last */
    {
        int topreg = live.fat[rr].holds[live.fat[rr].nholds];
        int thisind = live.fate[r].realind;
        live.fat[rr].holds[thisind] = topreg;
        live.fate[topreg].realind = thisind;
    }
    live.fate[r].status = INMEM;
    live.fate[r].realreg = -1;
}

static __forceinline void f_free_nreg(int r)
{
    int i = live.fat[r].nholds;

    while (i)
    {
        int vr;

        --i;
        vr = live.fat[r].holds[i];
        f_evict(vr);
    }
    Dif(live.fat[r].nholds != 0)
    {
        Logger::JitAbort(L"JIT: Failed to free nreg %d, nholds is %d\n", r, live.fat[r].nholds);
    }
}

/* Use with care! */
static __forceinline void f_isclean(int r)
{
    if (!f_isinreg(r))
        return;
    live.fate[r].status = CLEAN;
}

static __forceinline void f_disassociate(int r)
{
    f_isclean(r);
    f_evict(r);
}

static int f_alloc_reg(int r, int willclobber)
{
    int bestreg;
    int when;
    int i;
    int badness;
    bestreg = -1;
    when = 2000000000;
    for (i = N_FREGS; i--;)
    {
        badness = live.fat[i].touched;
        if (live.fat[i].nholds == 0)
            badness = 0;

        if (!live.fat[i].locked && badness < when)
        {
            bestreg = i;
            when = badness;
            if (live.fat[i].nholds == 0)
                break;
        }
    }
    Dif(bestreg == -1)
    abort();

    if (live.fat[bestreg].nholds > 0)
    {
        f_free_nreg(bestreg);
    }
    if (f_isinreg(r))
    {
        f_evict(r);
    }

    if (!willclobber)
    {
        if (live.fate[r].status != UNDEF)
        {
            #if USE_LONG_DOUBLE
            raw_fmov_ext_rm(bestreg, (uint)live.fate[r].mem);
            #else
            raw_fmov_rm(bestreg, (uint)live.fate[r].mem);
            #endif
        }
        live.fate[r].status = CLEAN;
    }
    else
    {
        live.fate[r].status = DIRTY;
    }
    live.fate[r].realreg = bestreg;
    live.fate[r].realind = live.fat[bestreg].nholds;
    live.fat[bestreg].touched = touchcnt++;
    live.fat[bestreg].holds[live.fat[bestreg].nholds] = r;
    live.fat[bestreg].nholds++;

    return bestreg;
}

static void f_unlock(int r)
{
    Dif(!live.fat[r].locked)
    Logger::JitAbort(L"unlock %d", r);
    live.fat[r].locked--;
}

static void f_setlock(int r)
{
    live.fat[r].locked++;
}

static __forceinline int f_readreg(int r)
{
    int n;
    int answer = -1;

    if (f_isinreg(r))
    {
        n = live.fate[r].realreg;
        answer = n;
    }
    /* either the value was in memory to start with, or it was evicted and
       is in memory now */
    if (answer < 0)
        answer = f_alloc_reg(r, 0);

    live.fat[answer].locked++;
    live.fat[answer].touched = touchcnt++;
    return answer;
}

static __forceinline void f_make_exclusive(int r, int clobber)
{
    freg_status oldstate;
    int rr = live.fate[r].realreg;
    int nr;
    int nind;
    int ndirt = 0;
    int i;

    if (!f_isinreg(r))
        return;
    if (live.fat[rr].nholds == 1)
        return;
    for (i = 0; i < live.fat[rr].nholds; i++)
    {
        int vr = live.fat[rr].holds[i];
        if (vr != r && live.fate[vr].status == DIRTY)
            ndirt++;
    }
    if (!ndirt && !live.fat[rr].locked)
    {
        /* Everything else is clean, so let's keep this register */
        for (i = 0; i < live.fat[rr].nholds; i++)
        {
            int vr = live.fat[rr].holds[i];
            if (vr != r)
            {
                f_evict(vr);
                i--; /* Try that index again! */
            }
        }
        Dif(live.fat[rr].nholds != 1)
        {
            Logger::Write(L"JIT: realreg %d holds %d (", rr, live.fat[rr].nholds);
            for (i = 0; i < live.fat[rr].nholds; i++)
            {
                Logger::Write(L"JIT: %d(%d,%d)", live.fat[rr].holds[i],
                    live.fate[live.fat[rr].holds[i]].realreg,
                    live.fate[live.fat[rr].holds[i]].realind);
            }
            Logger::Write(L"\n");
            Logger::JitAbort(L"x");
        }
        return;
    }

    /* We have to split the register */
    oldstate = live.fate[r];

    f_setlock(rr); /* Make sure this doesn't go away */
    /* Forget about r being in the register rr */
    f_disassociate(r);
    /* Get a new register, that we will clobber completely */
    nr = f_alloc_reg(r, 1);
    nind = live.fate[r].realind;
    if (!clobber)
        raw_fmov_rr(nr, rr);  /* Make another copy */
    live.fate[r] = oldstate;   /* Keep all the old state info */
    live.fate[r].realreg = nr;
    live.fate[r].realind = nind;
    f_unlock(rr);
}

static __forceinline int f_writereg(int r)
{
    int n;
    int answer = -1;

    f_make_exclusive(r, 1);
    if (f_isinreg(r))
    {
        n = live.fate[r].realreg;
        answer = n;
    }
    if (answer < 0)
    {
        answer = f_alloc_reg(r, 1);
    }
    live.fate[r].status = DIRTY;
    live.fat[answer].locked++;
    live.fat[answer].touched = touchcnt++;
    return answer;
}

static int f_rmw(int r)
{
    int n;

    f_make_exclusive(r, 0);
    if (f_isinreg(r))
    {
        n = live.fate[r].realreg;
    }
    else
        n = f_alloc_reg(r, 0);
    live.fate[r].status = DIRTY;
    live.fat[n].locked++;
    live.fat[n].touched = touchcnt++;
    return n;
}

static void fflags_into_flags_internal(uint tmp)
{
    int r;

    clobber_flags();
    r = f_readreg(FP_RESULT);
    raw_fflags_into_flags(r);
    f_unlock(r);
}

/********************************************************************
* CPU functions exposed to gencomp. Both CREATE and EMIT time      *
********************************************************************/

/*
 *  RULES FOR HANDLING REGISTERS:
 *
 *  * In the function headers, order the parameters
 *     - 1st registers written to
 *     - 2nd read/modify/write registers
 *     - 3rd registers read from
 *  * Before calling raw_*, you must call readreg, writereg or rmw for
 *    each register
 *  * The order for this is
 *     - 1st call remove_offset for all registers written to with size<4
 *     - 2nd call readreg for all registers read without offset
 *     - 3rd call rmw for all rmw registers
 *     - 4th call readreg_offset for all registers that can handle offsets
 *     - 5th call get_offset for all the registers from the previous step
 *     - 6th call writereg for all written-to registers
 *     - 7th call raw_*
 *     - 8th unlock all registers that were locked
 */

MIDFUNC(0, live_flags, ())
{
    live.flags_on_stack = TRASH;
    live.flags_in_flags = VALID;
    live.flags_are_important = 1;
}
MENDFUNC(0, live_flags, ())

MIDFUNC(0, dont_care_flags, ())
{
    live.flags_are_important = 0;
}
MENDFUNC(0, dont_care_flags, ())

/*
 * Copy m68k C flag into m68k X flag
 *
 * FIXME: This needs to be moved into the machdep
 * part of the source because it depends on what bit
 * is used to hold X.
 */
MIDFUNC(0, duplicate_carry, ())
{
    evict(FLAGX);
    make_flags_live_internal();
    COMPCALL(setcc_m) ((uint)live.state[FLAGX].mem + 1, 2);
}
MENDFUNC(0, duplicate_carry, ())

/*
 * Set host C flag from m68k X flag.
 *
 * FIXME: This needs to be moved into the machdep
 * part of the source because it depends on what bit
 * is used to hold X.
 */
MIDFUNC(0, restore_carry, ())
{
    if (!have_rat_stall)   /* Not a P6 core, i.e. no partial stalls */
    {
        bt_l_ri_noclobber(FLAGX, 8);
    }
    else    /* Avoid the stall the above creates.
               This is slow on non-P6, though.
             */
    {
        COMPCALL(rol_w_ri(FLAGX, 8));
        isclean(FLAGX);
        /* Why is the above faster than the below? */
        // raw_rol_b_mi((uint)live.state[FLAGX].mem,8);
    }
}
MENDFUNC(0, restore_carry, ())

MIDFUNC(0, start_needflags, ())
{
    needflags = 1;
}
MENDFUNC(0, start_needflags, ())

MIDFUNC(0, end_needflags, ())
{
    needflags = 0;
}
MENDFUNC(0, end_needflags, ())

MIDFUNC(0, make_flags_live, ())
{
    make_flags_live_internal();
}
MENDFUNC(0, make_flags_live, ())

MIDFUNC(1, fflags_into_flags, (W2 tmp))
{
    clobber_flags();
    fflags_into_flags_internal(tmp);
}
MENDFUNC(1, fflags_into_flags, (W2 tmp))

MIDFUNC(2, bt_l_ri, (R4 r, IMM i))   /* This is defined as only affecting C */
{
    int size = 4;
    if (i < 16)
        size = 2;
    CLOBBER_BT;
    r = readreg(r, size);
    raw_bt_l_ri(r, i);
    unlock(r);
}
MENDFUNC(2, bt_l_ri, (R4 r, IMM i)) /* This is defined as only affecting C */

MIDFUNC(2, bt_l_rr, (R4 r, R4 b))   /* This is defined as only affecting C */
{
    CLOBBER_BT;
    r = readreg(r, 4);
    b = readreg(b, 4);
    raw_bt_l_rr(r, b);
    unlock(r);
    unlock(b);
}
MENDFUNC(2, bt_l_rr, (R4 r, R4 b)) /* This is defined as only affecting C */

MIDFUNC(2, btc_l_ri, (RW4 r, IMM i))
{
    int size = 4;
    if (i < 16)
        size = 2;
    CLOBBER_BT;
    r = rmw(r, size, size);
    raw_btc_l_ri(r, i);
    unlock(r);
}
MENDFUNC(2, btc_l_ri, (RW4 r, IMM i))

MIDFUNC(2, btc_l_rr, (RW4 r, R4 b))
{
    CLOBBER_BT;
    b = readreg(b, 4);
    r = rmw(r, 4, 4);
    raw_btc_l_rr(r, b);
    unlock(r);
    unlock(b);
}
MENDFUNC(2, btc_l_rr, (RW4 r, R4 b))

MIDFUNC(2, btr_l_ri, (RW4 r, IMM i))
{
    int size = 4;
    if (i < 16)
        size = 2;
    CLOBBER_BT;
    r = rmw(r, size, size);
    raw_btr_l_ri(r, i);
    unlock(r);
}
MENDFUNC(2, btr_l_ri, (RW4 r, IMM i))

MIDFUNC(2, btr_l_rr, (RW4 r, R4 b))
{
    CLOBBER_BT;
    b = readreg(b, 4);
    r = rmw(r, 4, 4);
    raw_btr_l_rr(r, b);
    unlock(r);
    unlock(b);
}
MENDFUNC(2, btr_l_rr, (RW4 r, R4 b))

MIDFUNC(2, bts_l_ri, (RW4 r, IMM i))
{
    int size = 4;
    if (i < 16)
        size = 2;
    CLOBBER_BT;
    r = rmw(r, size, size);
    raw_bts_l_ri(r, i);
    unlock(r);
}
MENDFUNC(2, bts_l_ri, (RW4 r, IMM i))

MIDFUNC(2, bts_l_rr, (RW4 r, R4 b))
{
    CLOBBER_BT;
    b = readreg(b, 4);
    r = rmw(r, 4, 4);
    raw_bts_l_rr(r, b);
    unlock(r);
    unlock(b);
}
MENDFUNC(2, bts_l_rr, (RW4 r, R4 b))

MIDFUNC(2, mov_l_rm, (W4 d, IMM s))
{
    CLOBBER_MOV;
    d = writereg(d, 4);
    raw_mov_l_rm(d, s);
    unlock(d);
}
MENDFUNC(2, mov_l_rm, (W4 d, IMM s))

MIDFUNC(1, call_r, (R4 r))   /* Clobbering is implicit */
{
    r = readreg(r, 4);
    raw_call_r(r);
    unlock(r);
}
MENDFUNC(1, call_r, (R4 r)) /* Clobbering is implicit */

MIDFUNC(2, sub_l_mi, (IMM d, IMM s))
{
    CLOBBER_SUB;
    raw_sub_l_mi(d, s);
}
MENDFUNC(2, sub_l_mi, (IMM d, IMM s))

MIDFUNC(2, mov_l_mi, (IMM d, IMM s))
{
    CLOBBER_MOV;
    raw_mov_l_mi(d, s);
}
MENDFUNC(2, mov_l_mi, (IMM d, IMM s))

MIDFUNC(2, mov_w_mi, (IMM d, IMM s))
{
    CLOBBER_MOV;
    raw_mov_w_mi(d, s);
}
MENDFUNC(2, mov_w_mi, (IMM d, IMM s))

MIDFUNC(2, mov_b_mi, (IMM d, IMM s))
{
    CLOBBER_MOV;
    raw_mov_b_mi(d, s);
}
MENDFUNC(2, mov_b_mi, (IMM d, IMM s))

MIDFUNC(2, rol_b_ri, (RW1 r, IMM i))
{
    if (!i && !needflags)
        return;
    CLOBBER_ROL;
    r = rmw(r, 1, 1);
    raw_rol_b_ri(r, i);
    unlock(r);
}
MENDFUNC(2, rol_b_ri, (RW1 r, IMM i))

MIDFUNC(2, rol_w_ri, (RW2 r, IMM i))
{
    if (!i && !needflags)
        return;
    CLOBBER_ROL;
    r = rmw(r, 2, 2);
    raw_rol_w_ri(r, i);
    unlock(r);
}
MENDFUNC(2, rol_w_ri, (RW2 r, IMM i))

MIDFUNC(2, rol_l_ri, (RW4 r, IMM i))
{
    if (!i && !needflags)
        return;
    CLOBBER_ROL;
    r = rmw(r, 4, 4);
    raw_rol_l_ri(r, i);
    unlock(r);
}
MENDFUNC(2, rol_l_ri, (RW4 r, IMM i))

MIDFUNC(2, rol_l_rr, (RW4 d, R1 r))
{
    if (isconst(r))
    {
        COMPCALL(rol_l_ri) (d, (byte)live.state[r].val);
        return;
    }
    CLOBBER_ROL;
    r = readreg_specific(r, 1, SHIFTCOUNT_NREG);
    d = rmw(d, 4, 4);
    Dif(r != 1)
    {
        Logger::JitAbort(L"JIT: Illegal register %d in raw_rol_b\n", r);
    }
    raw_rol_l_rr(d, r);
    unlock(r);
    unlock(d);
}
MENDFUNC(2, rol_l_rr, (RW4 d, R1 r))

MIDFUNC(2, rol_w_rr, (RW2 d, R1 r))
{ /* Can only do this with r==1, i.e. cl */
    if (isconst(r))
    {
        COMPCALL(rol_w_ri) (d, (byte)live.state[r].val);
        return;
    }
    CLOBBER_ROL;
    r = readreg_specific(r, 1, SHIFTCOUNT_NREG);
    d = rmw(d, 2, 2);
    Dif(r != 1)
    {
        Logger::JitAbort(L"JIT: Illegal register %d in raw_rol_b\n", r);
    }
    raw_rol_w_rr(d, r);
    unlock(r);
    unlock(d);
}
MENDFUNC(2, rol_w_rr, (RW2 d, R1 r))

MIDFUNC(2, rol_b_rr, (RW1 d, R1 r))
{ /* Can only do this with r==1, i.e. cl */
    if (isconst(r))
    {
        COMPCALL(rol_b_ri) (d, (byte)live.state[r].val);
        return;
    }

    CLOBBER_ROL;
    r = readreg_specific(r, 1, SHIFTCOUNT_NREG);
    d = rmw(d, 1, 1);
    Dif(r != 1)
    {
        Logger::JitAbort(L"JIT: Illegal register %d in raw_rol_b\n", r);
    }
    raw_rol_b_rr(d, r);
    unlock(r);
    unlock(d);
}
MENDFUNC(2, rol_b_rr, (RW1 d, R1 r))

MIDFUNC(2, shll_l_rr, (RW4 d, R1 r))
{
    if (isconst(r))
    {
        COMPCALL(shll_l_ri) (d, (byte)live.state[r].val);
        return;
    }
    CLOBBER_SHLL;
    r = readreg_specific(r, 1, SHIFTCOUNT_NREG);
    d = rmw(d, 4, 4);
    Dif(r != 1)
    {
        Logger::JitAbort(L"JIT: Illegal register %d in raw_rol_b\n", r);
    }
    raw_shll_l_rr(d, r);
    unlock(r);
    unlock(d);
}
MENDFUNC(2, shll_l_rr, (RW4 d, R1 r))

MIDFUNC(2, shll_w_rr, (RW2 d, R1 r))
{ /* Can only do this with r==1, i.e. cl */
    if (isconst(r))
    {
        COMPCALL(shll_w_ri) (d, (byte)live.state[r].val);
        return;
    }
    CLOBBER_SHLL;
    r = readreg_specific(r, 1, SHIFTCOUNT_NREG);
    d = rmw(d, 2, 2);
    Dif(r != 1)
    {
        Logger::JitAbort(L"JIT: Illegal register %d in raw_shll_b\n", r);
    }
    raw_shll_w_rr(d, r);
    unlock(r);
    unlock(d);
}
MENDFUNC(2, shll_w_rr, (RW2 d, R1 r))

MIDFUNC(2, shll_b_rr, (RW1 d, R1 r))
{ /* Can only do this with r==1, i.e. cl */
    if (isconst(r))
    {
        COMPCALL(shll_b_ri) (d, (byte)live.state[r].val);
        return;
    }

    CLOBBER_SHLL;
    r = readreg_specific(r, 1, SHIFTCOUNT_NREG);
    d = rmw(d, 1, 1);
    Dif(r != 1)
    {
        Logger::JitAbort(L"JIT: Illegal register %d in raw_shll_b\n", r);
    }
    raw_shll_b_rr(d, r);
    unlock(r);
    unlock(d);
}
MENDFUNC(2, shll_b_rr, (RW1 d, R1 r))

MIDFUNC(2, ror_b_ri, (R1 r, IMM i))
{
    if (!i && !needflags)
        return;
    CLOBBER_ROR;
    r = rmw(r, 1, 1);
    raw_ror_b_ri(r, i);
    unlock(r);
}
MENDFUNC(2, ror_b_ri, (R1 r, IMM i))

MIDFUNC(2, ror_w_ri, (R2 r, IMM i))
{
    if (!i && !needflags)
        return;
    CLOBBER_ROR;
    r = rmw(r, 2, 2);
    raw_ror_w_ri(r, i);
    unlock(r);
}
MENDFUNC(2, ror_w_ri, (R2 r, IMM i))

MIDFUNC(2, ror_l_ri, (R4 r, IMM i))
{
    if (!i && !needflags)
        return;
    CLOBBER_ROR;
    r = rmw(r, 4, 4);
    raw_ror_l_ri(r, i);
    unlock(r);
}
MENDFUNC(2, ror_l_ri, (R4 r, IMM i))

MIDFUNC(2, ror_l_rr, (R4 d, R1 r))
{
    if (isconst(r))
    {
        COMPCALL(ror_l_ri) (d, (byte)live.state[r].val);
        return;
    }
    CLOBBER_ROR;
    r = readreg_specific(r, 1, SHIFTCOUNT_NREG);
    d = rmw(d, 4, 4);
    raw_ror_l_rr(d, r);
    unlock(r);
    unlock(d);
}
MENDFUNC(2, ror_l_rr, (R4 d, R1 r))

MIDFUNC(2, ror_w_rr, (R2 d, R1 r))
{
    if (isconst(r))
    {
        COMPCALL(ror_w_ri) (d, (byte)live.state[r].val);
        return;
    }
    CLOBBER_ROR;
    r = readreg_specific(r, 1, SHIFTCOUNT_NREG);
    d = rmw(d, 2, 2);
    raw_ror_w_rr(d, r);
    unlock(r);
    unlock(d);
}
MENDFUNC(2, ror_w_rr, (R2 d, R1 r))

MIDFUNC(2, ror_b_rr, (R1 d, R1 r))
{
    if (isconst(r))
    {
        COMPCALL(ror_b_ri) (d, (byte)live.state[r].val);
        return;
    }

    CLOBBER_ROR;
    r = readreg_specific(r, 1, SHIFTCOUNT_NREG);
    d = rmw(d, 1, 1);
    raw_ror_b_rr(d, r);
    unlock(r);
    unlock(d);
}
MENDFUNC(2, ror_b_rr, (R1 d, R1 r))

MIDFUNC(2, shrl_l_rr, (RW4 d, R1 r))
{
    if (isconst(r))
    {
        COMPCALL(shrl_l_ri) (d, (byte)live.state[r].val);
        return;
    }
    CLOBBER_SHRL;
    r = readreg_specific(r, 1, SHIFTCOUNT_NREG);
    d = rmw(d, 4, 4);
    Dif(r != 1)
    {
        Logger::JitAbort(L"JIT: Illegal register %d in raw_rol_b\n", r);
    }
    raw_shrl_l_rr(d, r);
    unlock(r);
    unlock(d);
}
MENDFUNC(2, shrl_l_rr, (RW4 d, R1 r))

MIDFUNC(2, shrl_w_rr, (RW2 d, R1 r))
{ /* Can only do this with r==1, i.e. cl */
    if (isconst(r))
    {
        COMPCALL(shrl_w_ri) (d, (byte)live.state[r].val);
        return;
    }
    CLOBBER_SHRL;
    r = readreg_specific(r, 1, SHIFTCOUNT_NREG);
    d = rmw(d, 2, 2);
    Dif(r != 1)
    {
        Logger::JitAbort(L"JIT: Illegal register %d in raw_shrl_b\n", r);
    }
    raw_shrl_w_rr(d, r);
    unlock(r);
    unlock(d);
}
MENDFUNC(2, shrl_w_rr, (RW2 d, R1 r))

MIDFUNC(2, shrl_b_rr, (RW1 d, R1 r))
{ /* Can only do this with r==1, i.e. cl */
    if (isconst(r))
    {
        COMPCALL(shrl_b_ri) (d, (byte)live.state[r].val);
        return;
    }

    CLOBBER_SHRL;
    r = readreg_specific(r, 1, SHIFTCOUNT_NREG);
    d = rmw(d, 1, 1);
    Dif(r != 1)
    {
        Logger::JitAbort(L"JIT: Illegal register %d in raw_shrl_b\n", r);
    }
    raw_shrl_b_rr(d, r);
    unlock(r);
    unlock(d);
}
MENDFUNC(2, shrl_b_rr, (RW1 d, R1 r))

MIDFUNC(2, shll_l_ri, (RW4 r, IMM i))
{
    if (!i && !needflags)
        return;
    if (isconst(r) && !needflags)
    {
        live.state[r].val <<= i;
        return;
    }
    CLOBBER_SHLL;
    r = rmw(r, 4, 4);
    raw_shll_l_ri(r, i);
    unlock(r);
}
MENDFUNC(2, shll_l_ri, (RW4 r, IMM i))

MIDFUNC(2, shll_w_ri, (RW2 r, IMM i))
{
    if (!i && !needflags)
        return;
    CLOBBER_SHLL;
    r = rmw(r, 2, 2);
    raw_shll_w_ri(r, i);
    unlock(r);
}
MENDFUNC(2, shll_w_ri, (RW2 r, IMM i))

MIDFUNC(2, shll_b_ri, (RW1 r, IMM i))
{
    if (!i && !needflags)
        return;
    CLOBBER_SHLL;
    r = rmw(r, 1, 1);
    raw_shll_b_ri(r, i);
    unlock(r);
}
MENDFUNC(2, shll_b_ri, (RW1 r, IMM i))

MIDFUNC(2, shrl_l_ri, (RW4 r, IMM i))
{
    if (!i && !needflags)
        return;
    if (isconst(r) && !needflags)
    {
        live.state[r].val >>= i;
        return;
    }
    CLOBBER_SHRL;
    r = rmw(r, 4, 4);
    raw_shrl_l_ri(r, i);
    unlock(r);
}
MENDFUNC(2, shrl_l_ri, (RW4 r, IMM i))

MIDFUNC(2, shrl_w_ri, (RW2 r, IMM i))
{
    if (!i && !needflags)
        return;
    CLOBBER_SHRL;
    r = rmw(r, 2, 2);
    raw_shrl_w_ri(r, i);
    unlock(r);
}
MENDFUNC(2, shrl_w_ri, (RW2 r, IMM i))

MIDFUNC(2, shrl_b_ri, (RW1 r, IMM i))
{
    if (!i && !needflags)
        return;
    CLOBBER_SHRL;
    r = rmw(r, 1, 1);
    raw_shrl_b_ri(r, i);
    unlock(r);
}
MENDFUNC(2, shrl_b_ri, (RW1 r, IMM i))

MIDFUNC(2, shra_l_ri, (RW4 r, IMM i))
{
    if (!i && !needflags)
        return;
    CLOBBER_SHRA;
    r = rmw(r, 4, 4);
    raw_shra_l_ri(r, i);
    unlock(r);
}
MENDFUNC(2, shra_l_ri, (RW4 r, IMM i))

MIDFUNC(2, shra_w_ri, (RW2 r, IMM i))
{
    if (!i && !needflags)
        return;
    CLOBBER_SHRA;
    r = rmw(r, 2, 2);
    raw_shra_w_ri(r, i);
    unlock(r);
}
MENDFUNC(2, shra_w_ri, (RW2 r, IMM i))

MIDFUNC(2, shra_b_ri, (RW1 r, IMM i))
{
    if (!i && !needflags)
        return;
    CLOBBER_SHRA;
    r = rmw(r, 1, 1);
    raw_shra_b_ri(r, i);
    unlock(r);
}
MENDFUNC(2, shra_b_ri, (RW1 r, IMM i))

MIDFUNC(2, shra_l_rr, (RW4 d, R1 r))
{
    if (isconst(r))
    {
        COMPCALL(shra_l_ri) (d, (byte)live.state[r].val);
        return;
    }
    CLOBBER_SHRA;
    r = readreg_specific(r, 1, SHIFTCOUNT_NREG);
    d = rmw(d, 4, 4);
    Dif(r != 1)
    {
        Logger::JitAbort(L"JIT: Illegal register %d in raw_rol_b\n", r);
    }
    raw_shra_l_rr(d, r);
    unlock(r);
    unlock(d);
}
MENDFUNC(2, shra_l_rr, (RW4 d, R1 r))

MIDFUNC(2, shra_w_rr, (RW2 d, R1 r))
{ /* Can only do this with r==1, i.e. cl */
    if (isconst(r))
    {
        COMPCALL(shra_w_ri) (d, (byte)live.state[r].val);
        return;
    }
    CLOBBER_SHRA;
    r = readreg_specific(r, 1, SHIFTCOUNT_NREG);
    d = rmw(d, 2, 2);
    Dif(r != 1)
    {
        Logger::JitAbort(L"JIT: Illegal register %d in raw_shra_b\n", r);
    }
    raw_shra_w_rr(d, r);
    unlock(r);
    unlock(d);
}
MENDFUNC(2, shra_w_rr, (RW2 d, R1 r))

MIDFUNC(2, shra_b_rr, (RW1 d, R1 r))
{ /* Can only do this with r==1, i.e. cl */
    if (isconst(r))
    {
        COMPCALL(shra_b_ri) (d, (byte)live.state[r].val);
        return;
    }

    CLOBBER_SHRA;
    r = readreg_specific(r, 1, SHIFTCOUNT_NREG);
    d = rmw(d, 1, 1);
    Dif(r != 1)
    {
        Logger::JitAbort(L"JIT: Illegal register %d in raw_shra_b\n", r);
    }
    raw_shra_b_rr(d, r);
    unlock(r);
    unlock(d);
}
MENDFUNC(2, shra_b_rr, (RW1 d, R1 r))

MIDFUNC(2, setcc, (W1 d, IMM cc))
{
    CLOBBER_SETCC;
    d = writereg(d, 1);
    raw_setcc(d, cc);
    unlock(d);
}
MENDFUNC(2, setcc, (W1 d, IMM cc))

MIDFUNC(2, setcc_m, (IMM d, IMM cc))
{
    CLOBBER_SETCC;
    raw_setcc_m(d, cc);
}
MENDFUNC(2, setcc_m, (IMM d, IMM cc))

MIDFUNC(3, cmov_b_rr, (RW1 d, R1 s, IMM cc))
{
    if (d == s)
        return;
    CLOBBER_CMOV;
    s = readreg(s, 1);
    d = rmw(d, 1, 1);
    raw_cmov_b_rr(d, s, cc);
    unlock(s);
    unlock(d);
}
MENDFUNC(3, cmov_b_rr, (RW1 d, R1 s, IMM cc))

MIDFUNC(3, cmov_w_rr, (RW2 d, R2 s, IMM cc))
{
    if (d == s)
        return;
    CLOBBER_CMOV;
    s = readreg(s, 2);
    d = rmw(d, 2, 2);
    raw_cmov_w_rr(d, s, cc);
    unlock(s);
    unlock(d);
}
MENDFUNC(3, cmov_w_rr, (RW2 d, R2 s, IMM cc))

MIDFUNC(3, cmov_l_rr, (RW4 d, R4 s, IMM cc))
{
    if (d == s)
        return;
    CLOBBER_CMOV;
    s = readreg(s, 4);
    d = rmw(d, 4, 4);
    raw_cmov_l_rr(d, s, cc);
    unlock(s);
    unlock(d);
}
MENDFUNC(3, cmov_l_rr, (RW4 d, R4 s, IMM cc))

MIDFUNC(1, setzflg_l, (RW4 r))
{
    if (setzflg_uses_bsf)
    {
        CLOBBER_BSF;
        r = rmw(r, 4, 4);
        raw_bsf_l_rr(r, r);
        unlock(r);
    }
    else
    {
        Dif(live.flags_in_flags != VALID)
        {
            Logger::JitAbort(L"JIT: setzflg() wanted flags in native flags, they are %d\n",
                live.flags_in_flags);
        }
        r = readreg(r, 4);
        {
            int f = writereg(S11, 4);
            int t = writereg(S12, 4);
            raw_flags_set_zero(f, r, t);
            unlock(f);
            unlock(r);
            unlock(t);
        }
    }
}
MENDFUNC(1, setzflg_l, (RW4 r))

MIDFUNC(3, cmov_l_rm, (RW4 d, IMM s, IMM cc))
{
    CLOBBER_CMOV;
    d = rmw(d, 4, 4);
    raw_cmov_l_rm(d, s, cc);
    unlock(d);
}
MENDFUNC(3, cmov_l_rm, (RW4 d, IMM s, IMM cc))

MIDFUNC(2, bsf_l_rr, (W4 d, R4 s))
{
    CLOBBER_BSF;
    s = readreg(s, 4);
    d = writereg(d, 4);
    raw_bsf_l_rr(d, s);
    unlock(s);
    unlock(d);
}
MENDFUNC(2, bsf_l_rr, (W4 d, R4 s))

MIDFUNC(2, imul_32_32, (RW4 d, R4 s))
{
    CLOBBER_MUL;
    s = readreg(s, 4);
    d = rmw(d, 4, 4);
    raw_imul_32_32(d, s);
    unlock(s);
    unlock(d);
}
MENDFUNC(2, imul_32_32, (RW4 d, R4 s))

MIDFUNC(2, imul_64_32, (RW4 d, RW4 s))
{
    CLOBBER_MUL;
    s = rmw_specific(s, 4, 4, MUL_NREG2);
    d = rmw_specific(d, 4, 4, MUL_NREG1);
    raw_imul_64_32(d, s);
    unlock(s);
    unlock(d);
}
MENDFUNC(2, imul_64_32, (RW4 d, RW4 s))

MIDFUNC(2, mul_64_32, (RW4 d, RW4 s))
{
    CLOBBER_MUL;
    s = rmw_specific(s, 4, 4, MUL_NREG2);
    d = rmw_specific(d, 4, 4, MUL_NREG1);
    raw_mul_64_32(d, s);
    unlock(s);
    unlock(d);
}
MENDFUNC(2, mul_64_32, (RW4 d, RW4 s))

MIDFUNC(2, sign_extend_16_rr, (W4 d, R2 s))
{
    int isrmw;

    if (isconst(s))
    {
        set_const(d, (int)(short)live.state[s].val);
        return;
    }

    CLOBBER_SE16;
    isrmw = (s == d);
    if (!isrmw)
    {
        s = readreg(s, 2);
        d = writereg(d, 4);
    }
    else    /* If we try to lock this twice, with different sizes, we
               are int trouble! */
    {
        s = d = rmw(s, 4, 2);
    }
    raw_sign_extend_16_rr(d, s);
    if (!isrmw)
    {
        unlock(d);
        unlock(s);
    }
    else
    {
        unlock(s);
    }
}
MENDFUNC(2, sign_extend_16_rr, (W4 d, R2 s))

MIDFUNC(2, sign_extend_8_rr, (W4 d, R1 s))
{
    int isrmw;

    if (isconst(s))
    {
        set_const(d, (int)(sbyte)live.state[s].val);
        return;
    }

    isrmw = (s == d);
    CLOBBER_SE8;
    if (!isrmw)
    {
        s = readreg(s, 1);
        d = writereg(d, 4);
    }
    else    /* If we try to lock this twice, with different sizes, we
               are int trouble! */
    {
        s = d = rmw(s, 4, 1);
    }

    raw_sign_extend_8_rr(d, s);

    if (!isrmw)
    {
        unlock(d);
        unlock(s);
    }
    else
    {
        unlock(s);
    }
}
MENDFUNC(2, sign_extend_8_rr, (W4 d, R1 s))

MIDFUNC(2, zero_extend_16_rr, (W4 d, R2 s))
{
    int isrmw;

    if (isconst(s))
    {
        set_const(d, (uint)(ushort)live.state[s].val);
        return;
    }

    isrmw = (s == d);
    CLOBBER_ZE16;
    if (!isrmw)
    {
        s = readreg(s, 2);
        d = writereg(d, 4);
    }
    else    /* If we try to lock this twice, with different sizes, we
               are int trouble! */
    {
        s = d = rmw(s, 4, 2);
    }
    raw_zero_extend_16_rr(d, s);
    if (!isrmw)
    {
        unlock(d);
        unlock(s);
    }
    else
    {
        unlock(s);
    }
}
MENDFUNC(2, zero_extend_16_rr, (W4 d, R2 s))

MIDFUNC(2, zero_extend_8_rr, (W4 d, R1 s))
{
    int isrmw;
    if (isconst(s))
    {
        set_const(d, (uint)(byte)live.state[s].val);
        return;
    }

    isrmw = (s == d);
    CLOBBER_ZE8;
    if (!isrmw)
    {
        s = readreg(s, 1);
        d = writereg(d, 4);
    }
    else    /* If we try to lock this twice, with different sizes, we
               are int trouble! */
    {
        s = d = rmw(s, 4, 1);
    }

    raw_zero_extend_8_rr(d, s);

    if (!isrmw)
    {
        unlock(d);
        unlock(s);
    }
    else
    {
        unlock(s);
    }
}
MENDFUNC(2, zero_extend_8_rr, (W4 d, R1 s))

MIDFUNC(2, mov_b_rr, (W1 d, R1 s))
{
    if (d == s)
        return;
    if (isconst(s))
    {
        COMPCALL(mov_b_ri) (d, (byte)live.state[s].val);
        return;
    }

    CLOBBER_MOV;
    s = readreg(s, 1);
    d = writereg(d, 1);
    raw_mov_b_rr(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, mov_b_rr, (W1 d, R1 s))

MIDFUNC(2, mov_w_rr, (W2 d, R2 s))
{
    if (d == s)
        return;
    if (isconst(s))
    {
        COMPCALL(mov_w_ri) (d, (ushort)live.state[s].val);
        return;
    }

    CLOBBER_MOV;
    s = readreg(s, 2);
    d = writereg(d, 2);
    raw_mov_w_rr(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, mov_w_rr, (W2 d, R2 s))

MIDFUNC(3, mov_l_rrm_indexed, (W4 d, R4 baser, R4 index))
{
    CLOBBER_MOV;
    baser = readreg(baser, 4);
    index = readreg(index, 4);
    d = writereg(d, 4);

    raw_mov_l_rrm_indexed(d, baser, index);
    unlock(d);
    unlock(baser);
    unlock(index);
}
MENDFUNC(3, mov_l_rrm_indexed, (W4 d, R4 baser, R4 index))

MIDFUNC(3, mov_w_rrm_indexed, (W2 d, R4 baser, R4 index))
{
    CLOBBER_MOV;
    baser = readreg(baser, 4);
    index = readreg(index, 4);
    d = writereg(d, 2);

    raw_mov_w_rrm_indexed(d, baser, index);
    unlock(d);
    unlock(baser);
    unlock(index);
}
MENDFUNC(3, mov_w_rrm_indexed, (W2 d, R4 baser, R4 index))

MIDFUNC(3, mov_b_rrm_indexed, (W1 d, R4 baser, R4 index))
{
    CLOBBER_MOV;
    baser = readreg(baser, 4);
    index = readreg(index, 4);
    d = writereg(d, 1);

    raw_mov_b_rrm_indexed(d, baser, index);

    unlock(d);
    unlock(baser);
    unlock(index);
}
MENDFUNC(3, mov_b_rrm_indexed, (W1 d, R4 baser, R4 index))

MIDFUNC(3, mov_l_mrr_indexed, (R4 baser, R4 index, R4 s))
{
    CLOBBER_MOV;
    baser = readreg(baser, 4);
    index = readreg(index, 4);
    s = readreg(s, 4);

    Dif(baser == s || index == s)
    Logger::JitAbort(L"mov_l_mrr_indexed");

    raw_mov_l_mrr_indexed(baser, index, s);
    unlock(s);
    unlock(baser);
    unlock(index);
}
MENDFUNC(3, mov_l_mrr_indexed, (R4 baser, R4 index, R4 s))

MIDFUNC(3, mov_w_mrr_indexed, (R4 baser, R4 index, R2 s))
{
    CLOBBER_MOV;
    baser = readreg(baser, 4);
    index = readreg(index, 4);
    s = readreg(s, 2);

    raw_mov_w_mrr_indexed(baser, index, s);
    unlock(s);
    unlock(baser);
    unlock(index);
}
MENDFUNC(3, mov_w_mrr_indexed, (R4 baser, R4 index, R2 s))

MIDFUNC(3, mov_b_mrr_indexed, (R4 baser, R4 index, R1 s))
{
    CLOBBER_MOV;
    s = readreg(s, 1);
    baser = readreg(baser, 4);
    index = readreg(index, 4);

    raw_mov_b_mrr_indexed(baser, index, s);
    unlock(s);
    unlock(baser);
    unlock(index);
}
MENDFUNC(3, mov_b_mrr_indexed, (R4 baser, R4 index, R1 s))

/* Read a long from base+4*index */
MIDFUNC(3, mov_l_rm_indexed, (W4 d, IMM base, R4 index))
{
    int indexreg = index;

    if (isconst(index))
    {
        COMPCALL(mov_l_rm) (d, base + 4 * live.state[index].val);
        return;
    }

    CLOBBER_MOV;
    index = readreg_offset(index, 4);
    base += get_offset(indexreg) * 4;
    d = writereg(d, 4);

    raw_mov_l_rm_indexed(d, base, index);
    unlock(index);
    unlock(d);
}
MENDFUNC(3, mov_l_rm_indexed, (W4 d, IMM base, R4 index))

/* read the long at the address contained in s+offset and store in d */
MIDFUNC(3, mov_l_rR, (W4 d, R4 s, IMM offset))
{
    if (isconst(s))
    {
        COMPCALL(mov_l_rm) (d, live.state[s].val + offset);
        return;
    }
    CLOBBER_MOV;
    s = readreg(s, 4);
    d = writereg(d, 4);

    raw_mov_l_rR(d, s, offset);
    unlock(d);
    unlock(s);
}
MENDFUNC(3, mov_l_rR, (W4 d, R4 s, IMM offset))

/* read the word at the address contained in s+offset and store in d */
MIDFUNC(3, mov_w_rR, (W2 d, R4 s, IMM offset))
{
    if (isconst(s))
    {
        COMPCALL(mov_w_rm) (d, live.state[s].val + offset);
        return;
    }
    CLOBBER_MOV;
    s = readreg(s, 4);
    d = writereg(d, 2);

    raw_mov_w_rR(d, s, offset);
    unlock(d);
    unlock(s);
}
MENDFUNC(3, mov_w_rR, (W2 d, R4 s, IMM offset))

/* read the word at the address contained in s+offset and store in d */
MIDFUNC(3, mov_b_rR, (W1 d, R4 s, IMM offset))
{
    if (isconst(s))
    {
        COMPCALL(mov_b_rm) (d, live.state[s].val + offset);
        return;
    }
    CLOBBER_MOV;
    s = readreg(s, 4);
    d = writereg(d, 1);

    raw_mov_b_rR(d, s, offset);
    unlock(d);
    unlock(s);
}
MENDFUNC(3, mov_b_rR, (W1 d, R4 s, IMM offset))

/* read the long at the address contained in s+offset and store in d */
MIDFUNC(3, mov_l_brR, (W4 d, R4 s, IMM offset))
{
    int sreg = s;
    if (isconst(s))
    {
        COMPCALL(mov_l_rm) (d, live.state[s].val + offset);
        return;
    }
    CLOBBER_MOV;
    s = readreg_offset(s, 4);
    offset += get_offset(sreg);
    d = writereg(d, 4);

    raw_mov_l_brR(d, s, offset);
    unlock(d);
    unlock(s);
}
MENDFUNC(3, mov_l_brR, (W4 d, R4 s, IMM offset))

/* read the word at the address contained in s+offset and store in d */
MIDFUNC(3, mov_w_brR, (W2 d, R4 s, IMM offset))
{
    int sreg = s;
    if (isconst(s))
    {
        COMPCALL(mov_w_rm) (d, live.state[s].val + offset);
        return;
    }
    CLOBBER_MOV;
    remove_offset(d, -1);
    s = readreg_offset(s, 4);
    offset += get_offset(sreg);
    d = writereg(d, 2);

    raw_mov_w_brR(d, s, offset);
    unlock(d);
    unlock(s);
}
MENDFUNC(3, mov_w_brR, (W2 d, R4 s, IMM offset))

/* read the word at the address contained in s+offset and store in d */
MIDFUNC(3, mov_b_brR, (W1 d, R4 s, IMM offset))
{
    int sreg = s;
    if (isconst(s))
    {
        COMPCALL(mov_b_rm) (d, live.state[s].val + offset);
        return;
    }
    CLOBBER_MOV;
    remove_offset(d, -1);
    s = readreg_offset(s, 4);
    offset += get_offset(sreg);
    d = writereg(d, 1);

    raw_mov_b_brR(d, s, offset);
    unlock(d);
    unlock(s);
}
MENDFUNC(3, mov_b_brR, (W1 d, R4 s, IMM offset))

MIDFUNC(3, mov_l_Ri, (R4 d, IMM i, IMM offset))
{
    int dreg = d;
    if (isconst(d))
    {
        COMPCALL(mov_l_mi) (live.state[d].val + offset, i);
        return;
    }

    CLOBBER_MOV;
    d = readreg_offset(d, 4);
    offset += get_offset(dreg);
    raw_mov_l_Ri(d, i, offset);
    unlock(d);
}
MENDFUNC(3, mov_l_Ri, (R4 d, IMM i, IMM offset))

MIDFUNC(3, mov_w_Ri, (R4 d, IMM i, IMM offset))
{
    int dreg = d;
    if (isconst(d))
    {
        COMPCALL(mov_w_mi) (live.state[d].val + offset, i);
        return;
    }

    CLOBBER_MOV;
    d = readreg_offset(d, 4);
    offset += get_offset(dreg);
    raw_mov_w_Ri(d, i, offset);
    unlock(d);
}
MENDFUNC(3, mov_w_Ri, (R4 d, IMM i, IMM offset))

MIDFUNC(3, mov_b_Ri, (R4 d, IMM i, IMM offset))
{
    int dreg = d;
    if (isconst(d))
    {
        COMPCALL(mov_b_mi) (live.state[d].val + offset, i);
        return;
    }

    CLOBBER_MOV;
    d = readreg_offset(d, 4);
    offset += get_offset(dreg);
    raw_mov_b_Ri(d, i, offset);
    unlock(d);
}
MENDFUNC(3, mov_b_Ri, (R4 d, IMM i, IMM offset))

/* Warning! OFFSET is byte sized only! */
MIDFUNC(3, mov_l_Rr, (R4 d, R4 s, IMM offset))
{
    if (isconst(d))
    {
        COMPCALL(mov_l_mr) (live.state[d].val + offset, s);
        return;
    }
    if (isconst(s))
    {
        COMPCALL(mov_l_Ri) (d, live.state[s].val, offset);
        return;
    }

    CLOBBER_MOV;
    s = readreg(s, 4);
    d = readreg(d, 4);

    raw_mov_l_Rr(d, s, offset);
    unlock(d);
    unlock(s);
}
MENDFUNC(3, mov_l_Rr, (R4 d, R4 s, IMM offset))

MIDFUNC(3, mov_w_Rr, (R4 d, R2 s, IMM offset))
{
    if (isconst(d))
    {
        COMPCALL(mov_w_mr) (live.state[d].val + offset, s);
        return;
    }
    if (isconst(s))
    {
        COMPCALL(mov_w_Ri) (d, (ushort)live.state[s].val, offset);
        return;
    }

    CLOBBER_MOV;
    s = readreg(s, 2);
    d = readreg(d, 4);
    raw_mov_w_Rr(d, s, offset);
    unlock(d);
    unlock(s);
}
MENDFUNC(3, mov_w_Rr, (R4 d, R2 s, IMM offset))

MIDFUNC(3, mov_b_Rr, (R4 d, R1 s, IMM offset))
{
    if (isconst(d))
    {
        COMPCALL(mov_b_mr) (live.state[d].val + offset, s);
        return;
    }
    if (isconst(s))
    {
        COMPCALL(mov_b_Ri) (d, (byte)live.state[s].val, offset);
        return;
    }

    CLOBBER_MOV;
    s = readreg(s, 1);
    d = readreg(d, 4);
    raw_mov_b_Rr(d, s, offset);
    unlock(d);
    unlock(s);
}
MENDFUNC(3, mov_b_Rr, (R4 d, R1 s, IMM offset))

MIDFUNC(3, lea_l_brr, (W4 d, R4 s, IMM offset))
{
    if (isconst(s))
    {
        COMPCALL(mov_l_ri) (d, live.state[s].val + offset);
        return;
    }
    #if USE_OFFSET
    if (d == s)
    {
        add_offset(d, offset);
        return;
    }
    #endif
    CLOBBER_LEA;
    s = readreg(s, 4);
    d = writereg(d, 4);
    raw_lea_l_brr(d, s, offset);
    unlock(d);
    unlock(s);
}
MENDFUNC(3, lea_l_brr, (W4 d, R4 s, IMM offset))

MIDFUNC(5, lea_l_brr_indexed, (W4 d, R4 s, R4 index, IMM factor, IMM offset))
{
    CLOBBER_LEA;
    s = readreg(s, 4);
    index = readreg(index, 4);
    d = writereg(d, 4);

    raw_lea_l_brr_indexed(d, s, index, factor, offset);
    unlock(d);
    unlock(index);
    unlock(s);
}
MENDFUNC(5, lea_l_brr_indexed, (W4 d, R4 s, R4 index, IMM factor, IMM offset))

/* write d to the long at the address contained in s+offset */
MIDFUNC(3, mov_l_bRr, (R4 d, R4 s, IMM offset))
{
    int dreg = d;
    if (isconst(d))
    {
        COMPCALL(mov_l_mr) (live.state[d].val + offset, s);
        return;
    }

    CLOBBER_MOV;
    s = readreg(s, 4);
    d = readreg_offset(d, 4);
    offset += get_offset(dreg);

    raw_mov_l_bRr(d, s, offset);
    unlock(d);
    unlock(s);
}
MENDFUNC(3, mov_l_bRr, (R4 d, R4 s, IMM offset))

/* write the word at the address contained in s+offset and store in d */
MIDFUNC(3, mov_w_bRr, (R4 d, R2 s, IMM offset))
{
    int dreg = d;

    if (isconst(d))
    {
        COMPCALL(mov_w_mr) (live.state[d].val + offset, s);
        return;
    }

    CLOBBER_MOV;
    s = readreg(s, 2);
    d = readreg_offset(d, 4);
    offset += get_offset(dreg);
    raw_mov_w_bRr(d, s, offset);
    unlock(d);
    unlock(s);
}
MENDFUNC(3, mov_w_bRr, (R4 d, R2 s, IMM offset))

MIDFUNC(3, mov_b_bRr, (R4 d, R1 s, IMM offset))
{
    int dreg = d;
    if (isconst(d))
    {
        COMPCALL(mov_b_mr) (live.state[d].val + offset, s);
        return;
    }

    CLOBBER_MOV;
    s = readreg(s, 1);
    d = readreg_offset(d, 4);
    offset += get_offset(dreg);
    raw_mov_b_bRr(d, s, offset);
    unlock(d);
    unlock(s);
}
MENDFUNC(3, mov_b_bRr, (R4 d, R1 s, IMM offset))

MIDFUNC(1, gen_bswap_32, (RW4 r))
{
    // int reg = r;

    if (isconst(r))
    {
        uint oldv = live.state[r].val;
        live.state[r].val = reverse32(oldv);
        return;
    }

    CLOBBER_SW32;
    r = rmw(r, 4, 4);
    raw_bswap_32(r);
    unlock(r);
}
MENDFUNC(1, gen_bswap_32, (RW4 r))

MIDFUNC(1, gen_bswap_16, (RW2 r))
{
    if (isconst(r))
    {
        uint oldv = live.state[r].val;
        live.state[r].val = ((oldv >> 8) & 0xff) | ((oldv << 8) & 0xff00) |
            (oldv & 0xffff0000);
        return;
    }

    CLOBBER_SW16;
    r = rmw(r, 2, 2);

    raw_bswap_16(r);
    unlock(r);
}
MENDFUNC(1, gen_bswap_16, (RW2 r))

MIDFUNC(2, mov_l_rr, (W4 d, R4 s))
{
    int olds;

    if (d == s)   /* How pointless! */
    {
        return;
    }
    if (isconst(s))
    {
        COMPCALL(mov_l_ri) (d, live.state[s].val);
        return;
    }
    #if USE_ALIAS
    olds = s;
    disassociate(d);
    s = readreg_offset(s, 4);
    live.state[d].realreg = s;
    live.state[d].realind = live.nat[s].nholds;
    live.state[d].val = live.state[olds].val;
    live.state[d].validsize = 4;
    live.state[d].dirtysize = 4;
    set_status(d, DIRTY);

    live.nat[s].holds[live.nat[s].nholds] = d;
    live.nat[s].nholds++;
    log_clobberreg(d);

    /* Logger::Write (L"JIT: Added %d to nreg %d(%d), now holds %d regs\n",
       d,s,live.state[d].realind,live.nat[s].nholds); */
    unlock(s);
    #else
    CLOBBER_MOV;
    s = readreg(s, 4);
    d = writereg(d, 4);

    raw_mov_l_rr(d, s);
    unlock(d);
    unlock(s);
    #endif
}
MENDFUNC(2, mov_l_rr, (W4 d, R4 s))

MIDFUNC(2, mov_l_mr, (IMM d, R4 s))
{
    if (isconst(s))
    {
        COMPCALL(mov_l_mi) (d, live.state[s].val);
        return;
    }
    CLOBBER_MOV;
    s = readreg(s, 4);

    raw_mov_l_mr(d, s);
    unlock(s);
}
MENDFUNC(2, mov_l_mr, (IMM d, R4 s))

MIDFUNC(2, mov_w_mr, (IMM d, R2 s))
{
    if (isconst(s))
    {
        COMPCALL(mov_w_mi) (d, (ushort)live.state[s].val);
        return;
    }
    CLOBBER_MOV;
    s = readreg(s, 2);

    raw_mov_w_mr(d, s);
    unlock(s);
}
MENDFUNC(2, mov_w_mr, (IMM d, R2 s))

MIDFUNC(2, mov_w_rm, (W2 d, IMM s))
{
    CLOBBER_MOV;
    d = writereg(d, 2);

    raw_mov_w_rm(d, s);
    unlock(d);
}
MENDFUNC(2, mov_w_rm, (W2 d, IMM s))

MIDFUNC(2, mov_b_mr, (IMM d, R1 s))
{
    if (isconst(s))
    {
        COMPCALL(mov_b_mi) (d, (byte)live.state[s].val);
        return;
    }

    CLOBBER_MOV;
    s = readreg(s, 1);

    raw_mov_b_mr(d, s);
    unlock(s);
}
MENDFUNC(2, mov_b_mr, (IMM d, R1 s))

MIDFUNC(2, mov_b_rm, (W1 d, IMM s))
{
    CLOBBER_MOV;
    d = writereg(d, 1);

    raw_mov_b_rm(d, s);
    unlock(d);
}
MENDFUNC(2, mov_b_rm, (W1 d, IMM s))

MIDFUNC(2, mov_l_ri, (W4 d, IMM s))
{
    set_const(d, s);
    return;
}
MENDFUNC(2, mov_l_ri, (W4 d, IMM s))

MIDFUNC(2, mov_w_ri, (W2 d, IMM s))
{
    CLOBBER_MOV;
    d = writereg(d, 2);

    raw_mov_w_ri(d, s);
    unlock(d);
}
MENDFUNC(2, mov_w_ri, (W2 d, IMM s))

MIDFUNC(2, mov_b_ri, (W1 d, IMM s))
{
    CLOBBER_MOV;
    d = writereg(d, 1);

    raw_mov_b_ri(d, s);
    unlock(d);
}
MENDFUNC(2, mov_b_ri, (W1 d, IMM s))

MIDFUNC(2, add_l_mi, (IMM d, IMM s))
{
    CLOBBER_ADD;
    raw_add_l_mi(d, s);
}
MENDFUNC(2, add_l_mi, (IMM d, IMM s))

MIDFUNC(2, add_w_mi, (IMM d, IMM s))
{
    CLOBBER_ADD;
    raw_add_w_mi(d, s);
}
MENDFUNC(2, add_w_mi, (IMM d, IMM s))

MIDFUNC(2, add_b_mi, (IMM d, IMM s))
{
    CLOBBER_ADD;
    raw_add_b_mi(d, s);
}
MENDFUNC(2, add_b_mi, (IMM d, IMM s))

MIDFUNC(2, test_l_ri, (R4 d, IMM i))
{
    CLOBBER_TEST;
    d = readreg(d, 4);

    raw_test_l_ri(d, i);
    unlock(d);
}
MENDFUNC(2, test_l_ri, (R4 d, IMM i))

MIDFUNC(2, test_l_rr, (R4 d, R4 s))
{
    CLOBBER_TEST;
    d = readreg(d, 4);
    s = readreg(s, 4);

    raw_test_l_rr(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, test_l_rr, (R4 d, R4 s))

MIDFUNC(2, test_w_rr, (R2 d, R2 s))
{
    CLOBBER_TEST;
    d = readreg(d, 2);
    s = readreg(s, 2);

    raw_test_w_rr(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, test_w_rr, (R2 d, R2 s))

MIDFUNC(2, test_b_rr, (R1 d, R1 s))
{
    CLOBBER_TEST;
    d = readreg(d, 1);
    s = readreg(s, 1);

    raw_test_b_rr(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, test_b_rr, (R1 d, R1 s))

MIDFUNC(2, and_l_ri, (RW4 d, IMM i))
{
    if (isconst(d) && !needflags)
    {
        live.state[d].val &= i;
        return;
    }

    CLOBBER_AND;
    d = rmw(d, 4, 4);

    raw_and_l_ri(d, i);
    unlock(d);
}
MENDFUNC(2, and_l_ri, (RW4 d, IMM i))

MIDFUNC(2, and_l, (RW4 d, R4 s))
{
    CLOBBER_AND;
    s = readreg(s, 4);
    d = rmw(d, 4, 4);

    raw_and_l(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, and_l, (RW4 d, R4 s))

MIDFUNC(2, and_w, (RW2 d, R2 s))
{
    CLOBBER_AND;
    s = readreg(s, 2);
    d = rmw(d, 2, 2);

    raw_and_w(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, and_w, (RW2 d, R2 s))

MIDFUNC(2, and_b, (RW1 d, R1 s))
{
    CLOBBER_AND;
    s = readreg(s, 1);
    d = rmw(d, 1, 1);

    raw_and_b(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, and_b, (RW1 d, R1 s))

MIDFUNC(2, or_l_ri, (RW4 d, IMM i))
{
    if (isconst(d) && !needflags)
    {
        live.state[d].val |= i;
        return;
    }
    CLOBBER_OR;
    d = rmw(d, 4, 4);

    raw_or_l_ri(d, i);
    unlock(d);
}
MENDFUNC(2, or_l_ri, (RW4 d, IMM i))

MIDFUNC(2, or_l, (RW4 d, R4 s))
{
    if (isconst(d) && isconst(s) && !needflags)
    {
        live.state[d].val |= live.state[s].val;
        return;
    }
    CLOBBER_OR;
    s = readreg(s, 4);
    d = rmw(d, 4, 4);

    raw_or_l(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, or_l, (RW4 d, R4 s))

MIDFUNC(2, or_w, (RW2 d, R2 s))
{
    CLOBBER_OR;
    s = readreg(s, 2);
    d = rmw(d, 2, 2);

    raw_or_w(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, or_w, (RW2 d, R2 s))

MIDFUNC(2, or_b, (RW1 d, R1 s))
{
    CLOBBER_OR;
    s = readreg(s, 1);
    d = rmw(d, 1, 1);

    raw_or_b(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, or_b, (RW1 d, R1 s))

MIDFUNC(2, adc_l, (RW4 d, R4 s))
{
    CLOBBER_ADC;
    s = readreg(s, 4);
    d = rmw(d, 4, 4);

    raw_adc_l(d, s);

    unlock(d);
    unlock(s);
}
MENDFUNC(2, adc_l, (RW4 d, R4 s))

MIDFUNC(2, adc_w, (RW2 d, R2 s))
{
    CLOBBER_ADC;
    s = readreg(s, 2);
    d = rmw(d, 2, 2);

    raw_adc_w(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, adc_w, (RW2 d, R2 s))

MIDFUNC(2, adc_b, (RW1 d, R1 s))
{
    CLOBBER_ADC;
    s = readreg(s, 1);
    d = rmw(d, 1, 1);

    raw_adc_b(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, adc_b, (RW1 d, R1 s))

MIDFUNC(2, add_l, (RW4 d, R4 s))
{
    if (isconst(s))
    {
        COMPCALL(add_l_ri) (d, live.state[s].val);
        return;
    }

    CLOBBER_ADD;
    s = readreg(s, 4);
    d = rmw(d, 4, 4);

    raw_add_l(d, s);

    unlock(d);
    unlock(s);
}
MENDFUNC(2, add_l, (RW4 d, R4 s))

MIDFUNC(2, add_w, (RW2 d, R2 s))
{
    if (isconst(s))
    {
        COMPCALL(add_w_ri) (d, (ushort)live.state[s].val);
        return;
    }

    CLOBBER_ADD;
    s = readreg(s, 2);
    d = rmw(d, 2, 2);

    raw_add_w(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, add_w, (RW2 d, R2 s))

MIDFUNC(2, add_b, (RW1 d, R1 s))
{
    if (isconst(s))
    {
        COMPCALL(add_b_ri) (d, (byte)live.state[s].val);
        return;
    }

    CLOBBER_ADD;
    s = readreg(s, 1);
    d = rmw(d, 1, 1);

    raw_add_b(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, add_b, (RW1 d, R1 s))

MIDFUNC(2, sub_l_ri, (RW4 d, IMM i))
{
    if (!i && !needflags)
        return;
    if (isconst(d) && !needflags)
    {
        live.state[d].val -= i;
        return;
    }
    #if USE_OFFSET
    if (!needflags)
    {
        add_offset(d, -(signed)i);
        return;
    }
    #endif

    CLOBBER_SUB;
    d = rmw(d, 4, 4);

    raw_sub_l_ri(d, i);
    unlock(d);
}
MENDFUNC(2, sub_l_ri, (RW4 d, IMM i))

MIDFUNC(2, sub_w_ri, (RW2 d, IMM i))
{
    if (!i && !needflags)
        return;

    CLOBBER_SUB;
    d = rmw(d, 2, 2);

    raw_sub_w_ri(d, i);
    unlock(d);
}
MENDFUNC(2, sub_w_ri, (RW2 d, IMM i))

MIDFUNC(2, sub_b_ri, (RW1 d, IMM i))
{
    if (!i && !needflags)
        return;

    CLOBBER_SUB;
    d = rmw(d, 1, 1);

    raw_sub_b_ri(d, i);

    unlock(d);
}
MENDFUNC(2, sub_b_ri, (RW1 d, IMM i))

MIDFUNC(2, add_l_ri, (RW4 d, IMM i))
{
    if (!i && !needflags)
        return;
    if (isconst(d) && !needflags)
    {
        live.state[d].val += i;
        return;
    }
    #if USE_OFFSET
    if (!needflags)
    {
        add_offset(d, i);
        return;
    }
    #endif
    CLOBBER_ADD;
    d = rmw(d, 4, 4);
    raw_add_l_ri(d, i);
    unlock(d);
}
MENDFUNC(2, add_l_ri, (RW4 d, IMM i))

MIDFUNC(2, add_w_ri, (RW2 d, IMM i))
{
    if (!i && !needflags)
        return;

    CLOBBER_ADD;
    d = rmw(d, 2, 2);

    raw_add_w_ri(d, i);
    unlock(d);
}
MENDFUNC(2, add_w_ri, (RW2 d, IMM i))

MIDFUNC(2, add_b_ri, (RW1 d, IMM i))
{
    if (!i && !needflags)
        return;

    CLOBBER_ADD;
    d = rmw(d, 1, 1);

    raw_add_b_ri(d, i);

    unlock(d);
}
MENDFUNC(2, add_b_ri, (RW1 d, IMM i))

MIDFUNC(2, sbb_l, (RW4 d, R4 s))
{
    CLOBBER_SBB;
    s = readreg(s, 4);
    d = rmw(d, 4, 4);

    raw_sbb_l(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, sbb_l, (RW4 d, R4 s))

MIDFUNC(2, sbb_w, (RW2 d, R2 s))
{
    CLOBBER_SBB;
    s = readreg(s, 2);
    d = rmw(d, 2, 2);

    raw_sbb_w(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, sbb_w, (RW2 d, R2 s))

MIDFUNC(2, sbb_b, (RW1 d, R1 s))
{
    CLOBBER_SBB;
    s = readreg(s, 1);
    d = rmw(d, 1, 1);

    raw_sbb_b(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, sbb_b, (RW1 d, R1 s))

MIDFUNC(2, sub_l, (RW4 d, R4 s))
{
    if (isconst(s))
    {
        COMPCALL(sub_l_ri) (d, live.state[s].val);
        return;
    }

    CLOBBER_SUB;
    s = readreg(s, 4);
    d = rmw(d, 4, 4);

    raw_sub_l(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, sub_l, (RW4 d, R4 s))

MIDFUNC(2, sub_w, (RW2 d, R2 s))
{
    if (isconst(s))
    {
        COMPCALL(sub_w_ri) (d, (ushort)live.state[s].val);
        return;
    }

    CLOBBER_SUB;
    s = readreg(s, 2);
    d = rmw(d, 2, 2);

    raw_sub_w(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, sub_w, (RW2 d, R2 s))

MIDFUNC(2, sub_b, (RW1 d, R1 s))
{
    if (isconst(s))
    {
        COMPCALL(sub_b_ri) (d, (byte)live.state[s].val);
        return;
    }

    CLOBBER_SUB;
    s = readreg(s, 1);
    d = rmw(d, 1, 1);

    raw_sub_b(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, sub_b, (RW1 d, R1 s))

MIDFUNC(2, cmp_l, (R4 d, R4 s))
{
    CLOBBER_CMP;
    s = readreg(s, 4);
    d = readreg(d, 4);

    raw_cmp_l(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, cmp_l, (R4 d, R4 s))

MIDFUNC(2, cmp_l_ri, (R4 r, IMM i))
{
    CLOBBER_CMP;
    r = readreg(r, 4);

    raw_cmp_l_ri(r, i);
    unlock(r);
}
MENDFUNC(2, cmp_l_ri, (R4 r, IMM i))

MIDFUNC(2, cmp_w, (R2 d, R2 s))
{
    CLOBBER_CMP;
    s = readreg(s, 2);
    d = readreg(d, 2);

    raw_cmp_w(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, cmp_w, (R2 d, R2 s))

MIDFUNC(2, cmp_b, (R1 d, R1 s))
{
    CLOBBER_CMP;
    s = readreg(s, 1);
    d = readreg(d, 1);

    raw_cmp_b(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, cmp_b, (R1 d, R1 s))

MIDFUNC(2, xor_l, (RW4 d, R4 s))
{
    CLOBBER_XOR;
    s = readreg(s, 4);
    d = rmw(d, 4, 4);

    raw_xor_l(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, xor_l, (RW4 d, R4 s))

MIDFUNC(2, xor_w, (RW2 d, R2 s))
{
    CLOBBER_XOR;
    s = readreg(s, 2);
    d = rmw(d, 2, 2);

    raw_xor_w(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, xor_w, (RW2 d, R2 s))

MIDFUNC(2, xor_b, (RW1 d, R1 s))
{
    CLOBBER_XOR;
    s = readreg(s, 1);
    d = rmw(d, 1, 1);

    raw_xor_b(d, s);
    unlock(d);
    unlock(s);
}
MENDFUNC(2, xor_b, (RW1 d, R1 s))

MIDFUNC(5, call_r_11, (W4 out1, R4 r, R4 in1, IMM osize, IMM isize))
{
    clobber_flags();
    remove_all_offsets();
    if (osize == 4)
    {
        if (out1 != in1 && out1 != r)
        {
            COMPCALL(forget_about) (out1);
        }
    }
    else
    {
        tomem_c(out1);
    }

    in1 = readreg_specific(in1, isize, REG_PAR1);
    r = readreg(r, 4);
    prepare_for_call_1();  /* This should ensure that there won't be
                              any need for swapping nregs in prepare_for_call_2
                            */
    #if USE_NORMAL_CALLING_CONVENTION
    raw_push_l_r(in1);
    #endif
    unlock(in1);
    unlock(r);

    prepare_for_call_2();
    raw_call_r(r);

    #if USE_NORMAL_CALLING_CONVENTION
    raw_inc_sp(4);
    #endif

    live.nat[REG_RESULT].holds[0] = out1;
    live.nat[REG_RESULT].nholds = 1;
    live.nat[REG_RESULT].touched = touchcnt++;

    live.state[out1].realreg = REG_RESULT;
    live.state[out1].realind = 0;
    live.state[out1].val = 0;
    live.state[out1].validsize = osize;
    live.state[out1].dirtysize = osize;
    set_status(out1, DIRTY);
}
MENDFUNC(5, call_r_11, (W4 out1, R4 r, R4 in1, IMM osize, IMM isize))

MIDFUNC(5, call_r_02, (R4 r, R4 in1, R4 in2, IMM isize1, IMM isize2))
{
    clobber_flags();
    remove_all_offsets();
    in1 = readreg_specific(in1, isize1, REG_PAR1);
    in2 = readreg_specific(in2, isize2, REG_PAR2);
    r = readreg(r, 4);
    prepare_for_call_1();  /* This should ensure that there won't be
                              any need for swapping nregs in prepare_for_call_2
                            */
    #if USE_NORMAL_CALLING_CONVENTION
    raw_push_l_r(in2);
    raw_push_l_r(in1);
    #endif
    unlock(r);
    unlock(in1);
    unlock(in2);
    prepare_for_call_2();
    raw_call_r(r);
    #if USE_NORMAL_CALLING_CONVENTION
    raw_inc_sp(8);
    #endif
}
MENDFUNC(5, call_r_02, (R4 r, R4 in1, R4 in2, IMM isize1, IMM isize2))

MIDFUNC(1, forget_about, (W4 r))
{
    if (isinreg(r))
        disassociate(r);
    live.state[r].val = 0;
    set_status(r, UNDEF);
}
MENDFUNC(1, forget_about, (W4 r))

MIDFUNC(0, nop, ())
{
    raw_nop();
}
MENDFUNC(0, nop, ())

MIDFUNC(1, f_forget_about, (FW r))
{
    if (f_isinreg(r))
        f_disassociate(r);
    live.fate[r].status = UNDEF;
}
MENDFUNC(1, f_forget_about, (FW r))

MIDFUNC(1, fmov_pi, (FW r))
{
    r = f_writereg(r);
    raw_fmov_pi(r);
    f_unlock(r);
}
MENDFUNC(1, fmov_pi, (FW r))

MIDFUNC(1, fmov_log10_2, (FW r))
{
    r = f_writereg(r);
    raw_fmov_log10_2(r);
    f_unlock(r);
}
MENDFUNC(1, fmov_log10_2, (FW r))

MIDFUNC(1, fmov_log2_e, (FW r))
{
    r = f_writereg(r);
    raw_fmov_log2_e(r);
    f_unlock(r);
}
MENDFUNC(1, fmov_log2_e, (FW r))

MIDFUNC(1, fmov_loge_2, (FW r))
{
    r = f_writereg(r);
    raw_fmov_loge_2(r);
    f_unlock(r);
}
MENDFUNC(1, fmov_loge_2, (FW r))

MIDFUNC(1, fmov_1, (FW r))
{
    r = f_writereg(r);
    raw_fmov_1(r);
    f_unlock(r);
}
MENDFUNC(1, fmov_1, (FW r))

MIDFUNC(1, fmov_0, (FW r))
{
    r = f_writereg(r);
    raw_fmov_0(r);
    f_unlock(r);
}
MENDFUNC(1, fmov_0, (FW r))

MIDFUNC(2, fmov_rm, (FW r, MEMR m))
{
    r = f_writereg(r);
    raw_fmov_rm(r, m);
    f_unlock(r);
}
MENDFUNC(2, fmov_rm, (FW r, MEMR m))

MIDFUNC(2, fmovi_rm, (FW r, MEMR m))
{
    r = f_writereg(r);
    raw_fmovi_rm(r, m);
    f_unlock(r);
}
MENDFUNC(2, fmovi_rm, (FW r, MEMR m))

MIDFUNC(3, fmovi_mrb, (MEMW m, FR r, double* bounds))
{
    r = f_readreg(r);
    raw_fmovi_mrb(m, r, bounds);
    f_unlock(r);
}
MENDFUNC(3, fmovi_mrb, (MEMW m, FR r, double* bounds))

MIDFUNC(2, fmovs_rm, (FW r, MEMR m))
{
    r = f_writereg(r);
    raw_fmovs_rm(r, m);
    f_unlock(r);
}
MENDFUNC(2, fmovs_rm, (FW r, MEMR m))

MIDFUNC(2, fmovs_mr, (MEMW m, FR r))
{
    r = f_readreg(r);
    raw_fmovs_mr(m, r);
    f_unlock(r);
}
MENDFUNC(2, fmovs_mr, (MEMW m, FR r))

MIDFUNC(1, fcuts_r, (FRW r))
{
    r = f_rmw(r);
    raw_fcuts_r(r);
    f_unlock(r);
}
MENDFUNC(1, fcuts_r, (FRW r))

MIDFUNC(1, fcut_r, (FRW r))
{
    r = f_rmw(r);
    raw_fcut_r(r);
    f_unlock(r);
}
MENDFUNC(1, fcut_r, (FRW r))

MIDFUNC(2, fmovl_ri, (FW r, IMMS i))
{
    r = f_writereg(r);
    raw_fmovl_ri(r, i);
    f_unlock(r);
}
MENDFUNC(2, fmovl_ri, (FW r, IMMS i))

MIDFUNC(2, fmovs_ri, (FW r, IMM i))
{
    r = f_writereg(r);
    raw_fmovs_ri(r, i);
    f_unlock(r);
}
MENDFUNC(2, fmovs_ri, (FW r, IMM i))

MIDFUNC(3, fmov_ri, (FW r, IMM i1, IMM i2))
{
    r = f_writereg(r);
    raw_fmov_ri(r, i1, i2);
    f_unlock(r);
}
MENDFUNC(3, fmov_ri, (FW r, IMM i1, IMM i2))

MIDFUNC(4, fmov_ext_ri, (FW r, IMM i1, IMM i2, IMM i3))
{
    r = f_writereg(r);
    raw_fmov_ext_ri(r, i1, i2, i3);
    f_unlock(r);
}
MENDFUNC(4, fmov_ext_ri, (FW r, IMM i1, IMM i2, IMM i3))

MIDFUNC(2, fmov_ext_mr, (MEMW m, FR r))
{
    r = f_readreg(r);
    raw_fmov_ext_mr(m, r);
    f_unlock(r);
}
MENDFUNC(2, fmov_ext_mr, (MEMW m, FR r))

MIDFUNC(2, fmov_mr, (MEMW m, FR r))
{
    r = f_readreg(r);
    raw_fmov_mr(m, r);
    f_unlock(r);
}
MENDFUNC(2, fmov_mr, (MEMW m, FR r))

MIDFUNC(2, fmov_ext_rm, (FW r, MEMR m))
{
    r = f_writereg(r);
    raw_fmov_ext_rm(r, m);
    f_unlock(r);
}
MENDFUNC(2, fmov_ext_rm, (FW r, MEMR m))

MIDFUNC(2, fmov_rr, (FW d, FR s))
{
    if (d == s)   /* How pointless! */
    {
        return;
    }
    #if USE_F_ALIAS
    f_disassociate(d);
    s = f_readreg(s);
    live.fate[d].realreg = s;
    live.fate[d].realind = live.fat[s].nholds;
    live.fate[d].status = DIRTY;
    live.fat[s].holds[live.fat[s].nholds] = d;
    live.fat[s].nholds++;
    f_unlock(s);
    #else
    s = f_readreg(s);
    d = f_writereg(d);
    raw_fmov_rr(d, s);
    f_unlock(s);
    f_unlock(d);
    #endif
}
MENDFUNC(2, fmov_rr, (FW d, FR s))

MIDFUNC(2, fldcw_m_indexed, (R4 index, IMM base))
{
    index = readreg(index, 4);

    raw_fldcw_m_indexed(index, base);
    unlock(index);
}
MENDFUNC(2, fldcw_m_indexed, (R4 index, IMM base))

MIDFUNC(1, ftst_r, (FR r))
{
    r = f_readreg(r);
    raw_ftst_r(r);
    f_unlock(r);
}
MENDFUNC(1, ftst_r, (FR r))

MIDFUNC(0, dont_care_fflags, ())
{
    f_disassociate(FP_RESULT);
}
MENDFUNC(0, dont_care_fflags, ())

MIDFUNC(2, fsqrt_rr, (FW d, FR s))
{
    s = f_readreg(s);
    d = f_writereg(d);
    raw_fsqrt_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, fsqrt_rr, (FW d, FR s))

MIDFUNC(2, fabs_rr, (FW d, FR s))
{
    s = f_readreg(s);
    d = f_writereg(d);
    raw_fabs_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, fabs_rr, (FW d, FR s))

MIDFUNC(2, frndint_rr, (FW d, FR s))
{
    s = f_readreg(s);
    d = f_writereg(d);
    raw_frndint_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, frndint_rr, (FW d, FR s))

MIDFUNC(2, fgetexp_rr, (FW d, FR s))
{
    s = f_readreg(s);
    d = f_writereg(d);
    raw_fgetexp_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, fgetexp_rr, (FW d, FR s))

MIDFUNC(2, fgetman_rr, (FW d, FR s))
{
    s = f_readreg(s);
    d = f_writereg(d);
    raw_fgetman_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, fgetman_rr, (FW d, FR s))

MIDFUNC(2, fsin_rr, (FW d, FR s))
{
    s = f_readreg(s);
    d = f_writereg(d);
    raw_fsin_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, fsin_rr, (FW d, FR s))

MIDFUNC(2, fcos_rr, (FW d, FR s))
{
    s = f_readreg(s);
    d = f_writereg(d);
    raw_fcos_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, fcos_rr, (FW d, FR s))

MIDFUNC(2, ftan_rr, (FW d, FR s))
{
    s = f_readreg(s);
    d = f_writereg(d);
    raw_ftan_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, ftan_rr, (FW d, FR s))

MIDFUNC(3, fsincos_rr, (FW d, FW c, FR s))
{
    s = f_readreg(s);  /* s for source */
    d = f_writereg(d); /* d for sine   */
    c = f_writereg(c); /* c for cosine */
    raw_fsincos_rr(d, c, s);
    f_unlock(s);
    f_unlock(d);
    f_unlock(c);
}
MENDFUNC(3, fsincos_rr, (FW d, FW c, FR s))

MIDFUNC(2, fscale_rr, (FRW d, FR s))
{
    s = f_readreg(s);
    d = f_rmw(d);
    raw_fscale_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, fscale_rr, (FRW d, FR s))

MIDFUNC(2, ftwotox_rr, (FW d, FR s))
{
    s = f_readreg(s);
    d = f_writereg(d);
    raw_ftwotox_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, ftwotox_rr, (FW d, FR s))

MIDFUNC(2, fetox_rr, (FW d, FR s))
{
    s = f_readreg(s);
    d = f_writereg(d);
    raw_fetox_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, fetox_rr, (FW d, FR s))

MIDFUNC(2, fetoxM1_rr, (FW d, FR s))
{
    s = f_readreg(s);
    d = f_writereg(d);
    raw_fetoxM1_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, fetoxM1_rr, (FW d, FR s))

MIDFUNC(2, ftentox_rr, (FW d, FR s))
{
    s = f_readreg(s);
    d = f_writereg(d);
    raw_ftentox_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, ftentox_rr, (FW d, FR s))

MIDFUNC(2, flog2_rr, (FW d, FR s))
{
    s = f_readreg(s);
    d = f_writereg(d);
    raw_flog2_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, flog2_rr, (FW d, FR s))

MIDFUNC(2, flogN_rr, (FW d, FR s))
{
    s = f_readreg(s);
    d = f_writereg(d);
    raw_flogN_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, flogN_rr, (FW d, FR s))

MIDFUNC(2, flogNP1_rr, (FW d, FR s))
{
    s = f_readreg(s);
    d = f_writereg(d);
    raw_flogNP1_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, flogNP1_rr, (FW d, FR s))

MIDFUNC(2, flog10_rr, (FW d, FR s))
{
    s = f_readreg(s);
    d = f_writereg(d);
    raw_flog10_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, flog10_rr, (FW d, FR s))

MIDFUNC(2, fasin_rr, (FW d, FR s))
{
    s = f_readreg(s);
    d = f_writereg(d);
    raw_fasin_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, fasin_rr, (FW d, FR s))

MIDFUNC(2, facos_rr, (FW d, FR s))
{
    s = f_readreg(s);
    d = f_writereg(d);
    raw_facos_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, facos_rr, (FW d, FR s))

MIDFUNC(2, fatan_rr, (FW d, FR s))
{
    s = f_readreg(s);
    d = f_writereg(d);
    raw_fatan_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, fatan_rr, (FW d, FR s))

MIDFUNC(2, fatanh_rr, (FW d, FR s))
{
    s = f_readreg(s);
    d = f_writereg(d);
    raw_fatanh_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, fatanh_rr, (FW d, FR s))

MIDFUNC(2, fsinh_rr, (FW d, FR s))
{
    s = f_readreg(s);
    d = f_writereg(d);
    raw_fsinh_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, fsinh_rr, (FW d, FR s))

MIDFUNC(2, fcosh_rr, (FW d, FR s))
{
    s = f_readreg(s);
    d = f_writereg(d);
    raw_fcosh_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, fcosh_rr, (FW d, FR s))

MIDFUNC(2, ftanh_rr, (FW d, FR s))
{
    s = f_readreg(s);
    d = f_writereg(d);
    raw_ftanh_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, ftanh_rr, (FW d, FR s))

MIDFUNC(2, fneg_rr, (FW d, FR s))
{
    s = f_readreg(s);
    d = f_writereg(d);
    raw_fneg_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, fneg_rr, (FW d, FR s))

MIDFUNC(2, fadd_rr, (FRW d, FR s))
{
    s = f_readreg(s);
    d = f_rmw(d);
    raw_fadd_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, fadd_rr, (FRW d, FR s))

MIDFUNC(2, fsub_rr, (FRW d, FR s))
{
    s = f_readreg(s);
    d = f_rmw(d);
    raw_fsub_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, fsub_rr, (FRW d, FR s))

MIDFUNC(2, fcmp_rr, (FR d, FR s))
{
    d = f_readreg(d);
    s = f_readreg(s);
    raw_fcmp_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, fcmp_rr, (FR d, FR s))

MIDFUNC(2, fdiv_rr, (FRW d, FR s))
{
    s = f_readreg(s);
    d = f_rmw(d);
    raw_fdiv_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, fdiv_rr, (FRW d, FR s))

MIDFUNC(2, frem_rr, (FRW d, FR s))
{
    s = f_readreg(s);
    d = f_rmw(d);
    raw_frem_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, frem_rr, (FRW d, FR s))

MIDFUNC(2, frem1_rr, (FRW d, FR s))
{
    s = f_readreg(s);
    d = f_rmw(d);
    raw_frem1_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, frem1_rr, (FRW d, FR s))

MIDFUNC(2, fmul_rr, (FRW d, FR s))
{
    s = f_readreg(s);
    d = f_rmw(d);
    raw_fmul_rr(d, s);
    f_unlock(s);
    f_unlock(d);
}
MENDFUNC(2, fmul_rr, (FRW d, FR s))

/********************************************************************
* Support functions exposed to gencomp. CREATE time                *
********************************************************************/

int kill_rodent(int r)
{
    return KILLTHERAT &&
           have_rat_stall &&
           (live.state[r].status == INMEM ||
        live.state[r].status == CLEAN ||
        live.state[r].status == ISCONST ||
        live.state[r].dirtysize == 4);
}

uint get_const(int r)
{
    #if USE_OPTIMIZER
    if (!reg_alloc_run)
    #endif
    Dif(!isconst(r))
    {
        Logger::JitAbort(L"JIT: Register %d should be constant, but isn't\n", r);
    }
    return live.state[r].val;
}

void sync_m68k_pc()
{
    if (m68k_pc_offset)
    {
        add_l_ri(PC_P, m68k_pc_offset);
        comp_pc_p += m68k_pc_offset;
        m68k_pc_offset = 0;
    }
}

/********************************************************************
* Support functions exposed to newcpu                              *
********************************************************************/

uint scratch[VREGS];
fptype fscratch[VFREGS];

void init_comp()
{
    int i;
    byte* cb = can_byte;
    byte* cw = can_word;
    byte* a_u = always_used;

    for (i = 0; i < VREGS; i++)
    {
        live.state[i].realreg = -1;
        live.state[i].needflush = NF_SCRATCH;
        live.state[i].val = 0;
        set_status(i, UNDEF);
    }

    for (i = 0; i < VFREGS; i++)
    {
        live.fate[i].status = UNDEF;
        live.fate[i].realreg = -1;
        live.fate[i].needflush = NF_SCRATCH;
    }

    for (i = 0; i < VREGS; i++)
    {
        if (i < 16)   /* First 16 registers map to 68k registers */
        {
            live.state[i].mem = ((uint*)&regs) + i;
            live.state[i].needflush = NF_TOMEM;
            set_status(i, INMEM);
        }
        else
            live.state[i].mem = scratch + i;
    }
    live.state[PC_P].mem = (uint*)&(regs.pc_p);
    live.state[PC_P].needflush = NF_TOMEM;
    set_const(PC_P, (uint)comp_pc_p);

    live.state[FLAGX].mem = &(regflags.x);
    live.state[FLAGX].needflush = NF_TOMEM;
    set_status(FLAGX, INMEM);

    live.state[FLAGTMP].mem = &(regflags.cznv);
    live.state[FLAGTMP].needflush = NF_TOMEM;
    set_status(FLAGTMP, INMEM);

    live.state[NEXT_HANDLER].needflush = NF_HANDLER;
    set_status(NEXT_HANDLER, UNDEF);

    for (i = 0; i < VFREGS; i++)
    {
        if (i < 8)   /* First 8 registers map to 68k FPU registers */
        {
            live.fate[i].mem = (uint*)(((fptype*)regs.fp) + i);
            live.fate[i].needflush = NF_TOMEM;
            live.fate[i].status = INMEM;
        }
        else if (i == FP_RESULT)
        {
            live.fate[i].mem = (uint*)(&regs.fp_result);
            live.fate[i].needflush = NF_TOMEM;
            live.fate[i].status = INMEM;
        }
        else
            live.fate[i].mem = (uint*)(fscratch + i);
    }

    for (i = 0; i < N_REGS; i++)
    {
        live.nat[i].touched = 0;
        live.nat[i].nholds = 0;
        live.nat[i].locked = 0;
        if (*cb == i)
        {
            live.nat[i].canbyte = 1;
            cb++;
        }
        else
            live.nat[i].canbyte = 0;
        if (*cw == i)
        {
            live.nat[i].canword = 1;
            cw++;
        }
        else
            live.nat[i].canword = 0;
        if (*a_u == i)
        {
            live.nat[i].locked = 1;
            a_u++;
        }
    }

    for (i = 0; i < N_FREGS; i++)
    {
        live.fat[i].touched = 0;
        live.fat[i].nholds = 0;
        live.fat[i].locked = 0;
    }

    touchcnt = 1;
    m68k_pc_offset = 0;
    live.flags_in_flags = TRASH;
    live.flags_on_stack = VALID;
    live.flags_are_important = 1;

    raw_fp_init();
}

static void vinton(int i, sbyte* vton, int depth)
{
    int n;
    int rr;

    Dif(vton[i] == -1)
    {
        Logger::JitAbort(L"JIT: Asked to load register %d, but nowhere to go\n", i);
    }
    n = vton[i];
    Dif(live.nat[n].nholds > 1)
    Logger::JitAbort(L"vinton");
    if (live.nat[n].nholds && depth < N_REGS)
    {
        vinton(live.nat[n].holds[0], vton, depth + 1);
    }
    if (!isinreg(i))
        return;  /* Oops --- got rid of that one in the recursive calls */
    rr = live.state[i].realreg;
    if (rr != n)
        mov_nregs(n, rr);
}

    #if JIT_DEBUG_DEF > 1
/* This is going to be, amongst other things, a more elaborate version of
   flush() */
static __forceinline void match_states(smallstate* s)
{
    sbyte vton[VREGS];
    sbyte ndone[N_REGS];
    int i;
    int again = 0;

    for (i = 0; i < VREGS; i++)
        vton[i] = -1;

    for (i = 0; i < N_REGS; i++)
        if (s->nat[i].validsize)
            vton[s->nat[i].holds] = i;

    flush_flags(); /* low level */
    sync_m68k_pc(); /* mid level */

    /* We don't do FREGS yet, so this is raw flush() code */
    for (i = 0; i < VFREGS; i++)
    {
        if (live.fate[i].needflush == NF_SCRATCH ||
            live.fate[i].status == CLEAN)
        {
            f_disassociate(i);
        }
    }
    for (i = 0; i < VFREGS; i++)
    {
        if (live.fate[i].needflush == NF_TOMEM &&
            live.fate[i].status == DIRTY)
        {
            f_evict(i);
        }
    }
    raw_fp_cleanup_drop();

    /* Now comes the fun part. First, we need to remove all offsets */
    for (i = 0; i < VREGS; i++)
        if (!isconst(i) && live.state[i].val)
            remove_offset(i, -1);

    /* Next, we evict everything that does not end up in registers,
       write back overly dirty registers, and write back constants */
    for (i = 0; i < VREGS; i++)
    {
        switch (live.state[i].status)
        {
            case ISCONST:
                if (i != PC_P)
                    writeback_const(i);
                break;
            case DIRTY:
                if (vton[i] == -1)
                {
                    evict(i);
                    break;
                }
                if (live.state[i].dirtysize > s->nat[vton[i]].dirtysize)
                    tomem(i);
            /* Fall-through! */
            case CLEAN:
                if (vton[i] == -1 ||
                live.state[i].validsize < s->nat[vton[i]].validsize)
                    evict(i);
                else
                    make_exclusive(i, 0, -1);
                break;
            case INMEM:
                break;
            case UNDEF:
                break;
            default:
                Logger::Write(L"JIT: Weird status: %d\n", live.state[i].status);
                abort();
        }
    }

    /* Quick consistency check */
    for (i = 0; i < VREGS; i++)
    {
        if (isinreg(i))
        {
            int n = live.state[i].realreg;

            if (live.nat[n].nholds != 1)
            {
                Logger::Write(L"JIT: Register %d isn't alone in nreg %d\n",
                    i, n);
                abort();
            }
            if (vton[i] == -1)
            {
                Logger::Write(L"JIT: Register %d is still in register, shouldn't be\n",
                    i);
                abort();
            }
        }
    }

    /* Now we need to shuffle things around so the VREGs are in the
       right N_REGs. */
    for (i = 0; i < VREGS; i++)
    {
        if (isinreg(i) && vton[i] != live.state[i].realreg)
            vinton(i, vton, 0);
    }

    /* And now we may need to load some registers from memory */
    for (i = 0; i < VREGS; i++)
    {
        int n = vton[i];
        if (n == -1)
        {
            Dif(isinreg(i))
            {
                Logger::Write(L"JIT: Register %d unexpectedly in nreg %d\n",
                    i, live.state[i].realreg);
                abort();
            }
        }
        else
        {
            switch (live.state[i].status)
            {
                case CLEAN:
                case DIRTY:
                    Dif(n != live.state[i].realreg)
                    abort();
                    break;
                case INMEM:
                    Dif(live.nat[n].nholds)
                    {
                        Logger::Write(L"JIT: natreg %d holds %d vregs, should be empty\n",
                        n, live.nat[n].nholds);
                    }
                    raw_mov_l_rm(n, (uint)live.state[i].mem);
                    live.state[i].validsize = 4;
                    live.state[i].dirtysize = 0;
                    live.state[i].realreg = n;
                    live.state[i].realind = 0;
                    live.state[i].val = 0;
                    live.state[i].is_swapped = 0;
                    live.nat[n].nholds = 1;
                    live.nat[n].holds[0] = i;

                    set_status(i, CLEAN);
                    break;
                case ISCONST:
                    if (i != PC_P)
                    {
                        Logger::Write(L"JIT: Got constant in matchstate for reg %d. Bad!\n", i);
                        abort();
                    }
                    break;
                case UNDEF:
                    break;
            }
        }
    }

    /* One last consistency check, and adjusting the states in live
       to those in s */
    for (i = 0; i < VREGS; i++)
    {
        int n = vton[i];
        switch (live.state[i].status)
        {
            case INMEM:
                if (n != -1)
                    abort();
                break;
            case ISCONST:
                if (i != PC_P)
                    abort();
                break;
            case CLEAN:
            case DIRTY:
                if (n == -1)
                    abort();
                if (live.state[i].dirtysize > s->nat[n].dirtysize)
                    abort;
                if (live.state[i].validsize < s->nat[n].validsize)
                    abort;
                live.state[i].dirtysize = s->nat[n].dirtysize;
                live.state[i].validsize = s->nat[n].validsize;
                if (live.state[i].dirtysize)
                    set_status(i, DIRTY);
                break;
            case UNDEF:
                break;
        }
        if (n != -1)
            live.nat[n].touched = touchcnt++;
    }
}
    #else
static __forceinline void match_states(smallstate* s)
{
    flush(1);
}
    #endif

/* Only do this if you really mean it! The next call should be to init!*/
void flush(int save_regs)
{
    int i;

    log_flush();
    flush_flags(); /* low level */
    sync_m68k_pc(); /* mid level */

    if (save_regs)
    {
        for (i = 0; i < VFREGS; i++)
        {
            if (live.fate[i].needflush == NF_SCRATCH ||
                live.fate[i].status == CLEAN)
            {
                f_disassociate(i);
            }
        }
        for (i = 0; i < VREGS; i++)
        {
            if (live.state[i].needflush == NF_TOMEM)
            {
                switch (live.state[i].status)
                {
                    case INMEM:
                        if (live.state[i].val)
                        {
                            raw_add_l_mi((uint)live.state[i].mem, live.state[i].val);
                            live.state[i].val = 0;
                        }
                        break;
                    case CLEAN:
                    case DIRTY:
                        remove_offset(i, -1);
                        tomem(i);
                        break;
                    case ISCONST:
                        if (i != PC_P)
                            writeback_const(i);
                        break;
                    default: break;
                }
                Dif(live.state[i].val && i != PC_P)
                {
                    Logger::Write(L"JIT: Register %d still has val %x\n",
                        i, live.state[i].val);
                }
            }
        }
        for (i = 0; i < VFREGS; i++)
        {
            if (live.fate[i].needflush == NF_TOMEM &&
                live.fate[i].status == DIRTY)
            {
                f_evict(i);
            }
        }
        raw_fp_cleanup_drop();
    }
    if (needflags)
    {
        Logger::Write(L"JIT: Warning! flush with needflags=1!\n");
    }

    lopt_emit_all();
}

static void flush_keepflags()
{
    int i;

    for (i = 0; i < VFREGS; i++)
    {
        if (live.fate[i].needflush == NF_SCRATCH ||
            live.fate[i].status == CLEAN)
        {
            f_disassociate(i);
        }
    }
    for (i = 0; i < VREGS; i++)
    {
        if (live.state[i].needflush == NF_TOMEM)
        {
            switch (live.state[i].status)
            {
                case INMEM:
                    /* Can't adjust the offset here --- that needs "add" */
                    break;
                case CLEAN:
                case DIRTY:
                    remove_offset(i, -1);
                    tomem(i);
                    break;
                case ISCONST:
                    if (i != PC_P)
                        writeback_const(i);
                    break;
                default: break;
            }
        }
    }
    for (i = 0; i < VFREGS; i++)
    {
        if (live.fate[i].needflush == NF_TOMEM &&
            live.fate[i].status == DIRTY)
        {
            f_evict(i);
        }
    }
    raw_fp_cleanup_drop();
    lopt_emit_all();
}

void freescratch()
{
    int i;
    for (i = 0; i < N_REGS; i++)
        if (live.nat[i].locked && i != 4)
            Logger::Write(L"JIT: Warning! %d is locked\n", i);

    for (i = 0; i < VREGS; i++)
        if (live.state[i].needflush == NF_SCRATCH)
        {
            forget_about(i);
        }

    for (i = 0; i < VFREGS; i++)
        if (live.fate[i].needflush == NF_SCRATCH)
        {
            f_forget_about(i);
        }
}

/********************************************************************
* Support functions, internal                                      *
********************************************************************/

static void align_target(uint a)
{
    lopt_emit_all();
    /* Fill with NOPs --- makes debugging with gdb easier */
    while ((uint)target & (a - 1))
        *target++ = 0x90;
}

static void flush_all()
{
    int i;

    log_flush();
    for (i = 0; i < VREGS; i++)
        if (live.state[i].status == DIRTY)
        {
            if (!call_saved[live.state[i].realreg])
            {
                tomem(i);
            }
        }
    for (i = 0; i < VFREGS; i++)
        if (f_isinreg(i))
            f_evict(i);
    raw_fp_cleanup_drop();
}

/* Make sure all registers that will get clobbered by a call are
   save and sound in memory */
static void prepare_for_call_1()
{
    flush_all();  /* If there are registers that don't get clobbered,
                   * we should be a bit more selective here */
}

/* We will call a C routine in a moment. That will clobber all registers,
   so we need to disassociate everything */
static void prepare_for_call_2()
{
    int i;
    for (i = 0; i < N_REGS; i++)
        if (!call_saved[i] && live.nat[i].nholds > 0)
            free_nreg(i);

    for (i = 0; i < N_FREGS; i++)
        if (live.fat[i].nholds > 0)
            f_free_nreg(i);

    live.flags_in_flags = TRASH;  /* Note: We assume we already rescued the
                                     flags at the very start of the call_r
                                     functions! */
}

/********************************************************************
* Memory access and related functions, CREATE time                 *
********************************************************************/

void register_branch(uint not_taken, uint taken, byte cond)
{
    next_pc_p = not_taken;
    taken_pc_p = taken;
    branch_cc = cond;
}

static uint get_handler_address(uint addr)
{
    // uint cl = cacheline(addr);
    blockinfo* bi = get_blockinfo_addr_new((void*)addr, 0);

    #if USE_OPTIMIZER
    if (!bi && reg_alloc_run)
        return 0;
    #endif
    return (uint) & (bi->direct_handler_to_use);
}

static uint get_handler(uint addr)
{
    // uint cl = cacheline(addr);
    blockinfo* bi = get_blockinfo_addr_new((void*)addr, 0);

    #if USE_OPTIMIZER
    if (!bi && reg_alloc_run)
        return 0;
    #endif
    return (uint)bi->direct_handler_to_use;
}

static void load_handler(int reg, uint addr)
{
    mov_l_rm(reg, get_handler_address(addr));
}

static __forceinline void writemem(int address, int source, int offset, int size, int tmp)
{
    int f = tmp;

    mov_l_rr(f, address);
    shrl_l_ri(f, 16);   /* The index into the mem bank table */
    mov_l_rm_indexed(f, (uint)mem_banks, f);
    /* Now f holds a pointer to the actual membank */
    mov_l_rR(f, f, offset);
    /* Now f holds the address of the b/w/lput function */
    call_r_02(f, address, source, 4, size);
    forget_about(tmp);
}

void writebyte(int address, int source, int tmp)
{
    writemem(address, source, 20, 1, tmp);
}

void writeword(int address, int source, int tmp)
{
    writemem(address, source, 16, 2, tmp);
}

void writelong(int address, int source, int tmp)
{
    writemem(address, source, 12, 4, tmp);
}

static __forceinline void readmem(int address, int dest, int offset, int size, int tmp)
{
    int f = tmp;

    mov_l_rr(f, address);
    shrl_l_ri(f, 16);   /* The index into the mem bank table */
    mov_l_rm_indexed(f, (uint)mem_banks, f);
    /* Now f holds a pointer to the actual membank */
    mov_l_rR(f, f, offset);
    /* Now f holds the address of the b/w/lget function */
    call_r_11(dest, f, address, size, 4);
    forget_about(tmp);
}

void readbyte(int address, int dest, int tmp)
{
    readmem(address, dest, 8, 1, tmp);
}

void readword(int address, int dest, int tmp)
{
    readmem(address, dest, 4, 2, tmp);
}

void readlong(int address, int dest, int tmp)
{
    readmem(address, dest, 0, 4, tmp);
}

/* This one might appear a bit odd... */
static __forceinline void get_n_addr_old(int address, int dest, int tmp)
{
    readmem(address, dest, 24, 4, tmp);
}

void get_n_addr(int address, int dest, int tmp)
{
    get_n_addr_old(address, dest, tmp);
}

void get_n_addr_jmp(int address, int dest, int tmp)
{
    #if 0 /* For this, we need to get the same address as the rest of UAE
             would --- otherwise we end up translating everything twice */
    get_n_addr(address, dest, tmp);
    #else
    int f = tmp;
    if (address != dest)
        f = dest;
    mov_l_rr(f, address);
    shrl_l_ri(f, 16);   /* The index into the baseaddr bank table */
    mov_l_rm_indexed(dest, (uint)baseaddr, f);
    add_l(dest, address);
    and_l_ri(dest, ~1);
    forget_about(tmp);
    #endif
}

/* base, target and tmp are registers, but dp is the actual opcode extension word */
void calc_disp_ea_020(int base, uint dp, int target, int tmp)
{
    int reg = (dp >> 12) & 15;
    int regd_shift = (dp >> 9) & 3;

    if (dp & 0x100)
    {
        int ignorebase = (dp & 0x80);
        int ignorereg = (dp & 0x40);
        int addbase = 0;
        int outer = 0;

        if ((dp & 0x30) == 0x20)
            addbase = (int)(short)comp_get_iword((m68k_pc_offset += 2) - 2);
        if ((dp & 0x30) == 0x30)
            addbase = comp_get_ilong((m68k_pc_offset += 4) - 4);

        if ((dp & 0x3) == 0x2)
            outer = (int)(short)comp_get_iword((m68k_pc_offset += 2) - 2);
        if ((dp & 0x3) == 0x3)
            outer = comp_get_ilong((m68k_pc_offset += 4) - 4);

        if ((dp & 0x4) == 0)    /* add regd *before* the get_long */
        {
            if (!ignorereg)
            {
                if ((dp & 0x800) == 0)
                    sign_extend_16_rr(target, reg);
                else
                    mov_l_rr(target, reg);
                shll_l_ri(target, regd_shift);
            }
            else
                mov_l_ri(target, 0);

            /* target is now regd */
            if (!ignorebase)
                add_l(target, base);
            add_l_ri(target, addbase);
            if (dp & 0x03)
                readlong(target, target, tmp);
        }
        else     /* do the getlong first, then add regd */
        {
            if (!ignorebase)
            {
                mov_l_rr(target, base);
                add_l_ri(target, addbase);
            }
            else
                mov_l_ri(target, addbase);
            if (dp & 0x03)
                readlong(target, target, tmp);

            if (!ignorereg)
            {
                if ((dp & 0x800) == 0)
                    sign_extend_16_rr(tmp, reg);
                else
                    mov_l_rr(tmp, reg);
                shll_l_ri(tmp, regd_shift);
                /* tmp is now regd */
                add_l(target, tmp);
            }
        }
        add_l_ri(target, outer);
    }
    else   /* 68000 version */
    {
        if ((dp & 0x800) == 0)   /* Sign extend */
        {
            sign_extend_16_rr(target, reg);
            lea_l_brr_indexed(target, base, target, regd_shift, (int)(sbyte)dp);
        }
        else
        {
            lea_l_brr_indexed(target, base, reg, regd_shift, (int)(sbyte)dp);
        }
    }
    forget_about(tmp);
}

static __forceinline uint cft_map(uint f)
{
    return ((f >> 8) & 255) | ((f & 255) << 8);
}

void set_cache_state(int enabled)
{
    if (enabled != letit)
        flush_icache_hard(0, 3);
    letit = enabled;
}

int get_cache_state()
{
    return letit;
}

uint get_jitted_size()
{
    if (compiled_code)
        return current_compile_p - compiled_code;
    return 0;
}

void alloc_cache()
{
    if (compiled_code)
    {
        flush_icache_hard(0, 3);
        cache_free(compiled_code);
    }
    if (popallspace == nullptr)
        popallspace = cache_alloc(1024);
    compiled_code = nullptr;
    if (g_curr_conf.jit_cache_size == 0)
        return;

    while (!compiled_code && g_curr_conf.jit_cache_size)
    {
        compiled_code = cache_alloc(g_curr_conf.jit_cache_size * 1024);
        if (!compiled_code)
            g_curr_conf.jit_cache_size /= 2;
    }
    if (compiled_code)
    {
        max_compile_start = compiled_code + g_curr_conf.jit_cache_size * 1024 - BYTES_PER_INST;
        current_compile_p = compiled_code;
    }
}

static void calc_checksum(blockinfo* bi, uint* c1, uint* c2)
{
    uint k1 = 0;
    uint k2 = 0;
    int len = bi->len;
    uint tmp = bi->min_pcp;
    uint* pos;

    len += (tmp & 3);
    tmp &= (~3);
    pos = (uint*)tmp;

    if (len < 0 || len > MAX_CHECKSUM_LEN)
    {
        *c1 = 0;
        *c2 = 0;
    }
    else
    {
        while (len > 0)
        {
            k1 += *pos;
            k2 ^= *pos;
            pos++;
            len -= 4;
        }
        *c1 = k1;
        *c2 = k2;
    }
}

static void show_checksum(blockinfo* bi)
{
    // uint k1 = 0;
    // uint k2 = 0;
    int len = bi->len;
    uint tmp = (uint)bi->pc_p;
    uint* pos;

    len += (tmp & 3);
    tmp &= (~3);
    pos = (uint*)tmp;

    if (len < 0 || len > MAX_CHECKSUM_LEN)
    {
        return;
    }
    else
    {
        while (len > 0)
        {
            Logger::Write(L"%08x ", *pos);
            pos++;
            len -= 4;
        }
        Logger::Write(L" bla\n");
    }
}

int check_for_cache_miss()
{
    blockinfo* bi = get_blockinfo_addr(regs.pc_p);

    if (bi)
    {
        int cl = cacheline(regs.pc_p);
        if (bi != cache_tags[cl + 1].bi)
        {
            raise_in_cl_list(bi);
            return 1;
        }
    }
    return 0;
}

static void recompile_block()
{
    /* An existing block's countdown code has expired. We need to make
       sure that execute_normal doesn't refuse to recompile due to a
       perceived cache miss... */
    blockinfo* bi = get_blockinfo_addr(regs.pc_p);

    Dif(!bi)
    Logger::JitAbort(L"recompile_block");
    raise_in_cl_list(bi);
    execute_normal();
    return;
}

static void cache_miss()
{
    blockinfo* bi = get_blockinfo_addr(regs.pc_p);
    uint cl = cacheline(regs.pc_p);
    blockinfo* bi2 = get_blockinfo(cl);

    if (!bi)
    {
        execute_normal(); /* Compile this block now */
        return;
    }
    Dif(!bi2 || bi == bi2)
    {
        Logger::JitAbort(L"Unexplained cache miss %p %p\n", bi, bi2);
    }
    raise_in_cl_list(bi);
    return;
}

static void check_checksum()
{
    blockinfo* bi = get_blockinfo_addr(regs.pc_p);
    uint cl = cacheline(regs.pc_p);
    blockinfo* bi2 = get_blockinfo(cl);

    uint c1, c2;

    checksum_count++;
    /* These are not the droids you are looking for...  */
    if (!bi)
    {
        /* Whoever is the primary target is in a dormant state, but
           calling it was accidental, and we should just compile this
           new block */
        execute_normal();
        return;
    }
    if (bi != bi2)
    {
        /* The block was hit accidentally, but it does exist. Cache miss */
        cache_miss();
        return;
    }

    if (bi->c1 || bi->c2)
        calc_checksum(bi, &c1, &c2);
    else
    {
        c1 = c2 = 1;  /* Make sure it doesn't match */
    }
    if (c1 == bi->c1 && c2 == bi->c2)
    {
        /* This block is still OK. So we reactivate. Of course, that
           means we have to move it into the needs-to-be-flushed list */
        bi->handler_to_use = bi->handler;
        set_dhtu(bi, bi->direct_handler);

        /*	Logger::Write (L"JIT: reactivate %p/%p (%x %x/%x %x)\n",bi,bi->pc_p,
           c1,c2,bi->c1,bi->c2);*/
        remove_from_list(bi);
        add_to_active(bi);
        raise_in_cl_list(bi);
    }
    else
    {
        /* This block actually changed. We need to invalidate it,
           and set it up to be recompiled */
        /* Logger::Write (L"JIT: discard %p/%p (%x %x/%x %x)\n",bi,bi->pc_p,
           c1,c2,bi->c1,bi->c2); */
        invalidate_block(bi);
        raise_in_cl_list(bi);
        execute_normal();
    }
}

static __forceinline void create_popalls()
{
    int i, r;

    current_compile_p = popallspace;
    set_target(current_compile_p);
    #if USE_PUSH_POP
    /* If we can't use gcc inline assembly, we need to pop some
       registers before jumping back to the various get-out routines.
       This generates the code for it.
     */
    popall_do_nothing = current_compile_p;
    for (i = 0; i < N_REGS; i++)
    {
        if (need_to_preserve[i])
            raw_pop_l_r(i);
    }
    raw_jmp((uint)do_nothing);
    align_target(32);

    popall_execute_normal = get_target();
    for (i = 0; i < N_REGS; i++)
    {
        if (need_to_preserve[i])
            raw_pop_l_r(i);
    }
    raw_jmp((uint)execute_normal);
    align_target(32);

    popall_cache_miss = get_target();
    for (i = 0; i < N_REGS; i++)
    {
        if (need_to_preserve[i])
            raw_pop_l_r(i);
    }
    raw_jmp((uint)cache_miss);
    align_target(32);

    popall_recompile_block = get_target();
    for (i = 0; i < N_REGS; i++)
    {
        if (need_to_preserve[i])
            raw_pop_l_r(i);
    }
    raw_jmp((uint)recompile_block);
    align_target(32);

    popall_exec_nostats = get_target();
    for (i = 0; i < N_REGS; i++)
    {
        if (need_to_preserve[i])
            raw_pop_l_r(i);
    }
    raw_jmp((uint)exec_nostats);
    align_target(32);

    popall_check_checksum = get_target();
    for (i = 0; i < N_REGS; i++)
    {
        if (need_to_preserve[i])
            raw_pop_l_r(i);
    }
    raw_jmp((uint)check_checksum);
    align_target(32);

    current_compile_p = get_target();
    #else
    popall_exec_nostats = exec_nostats;
    popall_execute_normal = execute_normal;
    popall_cache_miss = cache_miss;
    popall_recompile_block = recompile_block;
    popall_do_nothing = do_nothing;
    popall_check_checksum = check_checksum;
    #endif

    /* And now, the code to do the matching pushes and then jump
       into a handler routine */
    pushall_call_handler = get_target();
    #if USE_PUSH_POP
    for (i = N_REGS; i--;)
    {
        if (need_to_preserve[i])
            raw_push_l_r(i);
    }
    #endif
    r = REG_PC_TMP;
    raw_mov_l_rm(r, (uint) & regs.pc_p);
    raw_and_l_ri(r, TAGMASK);
    raw_jmp_m_indexed((uint)cache_tags, r, 4);
}

static __forceinline void reset_lists()
{
    int i;

    for (i = 0; i < MAX_HOLD_BI; i++)
        hold_bi[i] = nullptr;
    active = nullptr;
    dormant = nullptr;
}

static void prepare_block(blockinfo* bi)
{
    int i;

    set_target(current_compile_p);
    align_target(32);
    bi->direct_pen = (cpuop_func*)get_target();
    raw_mov_l_rm(0, (uint) & (bi->pc_p));
    raw_mov_l_mr((uint) & regs.pc_p, 0);
    raw_jmp((uint)popall_execute_normal);

    align_target(32);
    bi->direct_pcc = (cpuop_func*)get_target();
    raw_mov_l_rm(0, (uint) & (bi->pc_p));
    raw_mov_l_mr((uint) & regs.pc_p, 0);
    raw_jmp((uint)popall_check_checksum);

    align_target(32);
    current_compile_p = get_target();

    bi->deplist = nullptr;
    for (i = 0; i < 2; i++)
    {
        bi->dep[i].prev_p = nullptr;
        bi->dep[i].next = nullptr;
    }
    bi->env = default_ss;
    bi->status = BI_NEW;
    bi->havestate = 0;
    // bi->env=empty_ss;
}

void compemu_reset()
{
    set_cache_state(0);
}

void build_comp()
{
    int i;
    // int jumpcount = 0;
    uint opcode;
    const struct comptbl* tbl = op_smalltbl_0_comp_ff;
    const struct comptbl* nftbl = op_smalltbl_0_comp_nf;
    int count;
    #ifdef NOFLAGS_SUPPORT
    struct comptbl* nfctbl = (g_curr_conf.cpu_level >= 5 ? op_smalltbl_0_nf
            : g_curr_conf.cpu_level == 4 ? op_smalltbl_1_nf
                  : (g_curr_conf.cpu_level == 2 || g_curr_conf.cpu_level == 3) ? op_smalltbl_2_nf
                        : g_curr_conf.cpu_level == 1 ? op_smalltbl_3_nf
                              : !g_curr_conf.cpu_compatible ? op_smalltbl_4_nf
                                    : op_smalltbl_5_nf);
    #endif
    raw_init_cpu();

    if (Logging::JIT_DEBUG)
        Logger::Write(L"JIT: Building Compiler function table\n");

    for (opcode = 0; opcode < 65536; opcode++)
    {
        #ifdef NOFLAGS_SUPPORT
        nfcpufunctbl[opcode] = op_illg;
        #endif
        compfunctbl[opcode] = nullptr;
        nfcompfunctbl[opcode] = nullptr;
        prop[opcode].use_flags = 0x1f;
        prop[opcode].set_flags = 0x1f;
        prop[opcode].is_jump = 1;
    }

    for (i = 0; tbl[i].opcode < 65536; i++)
    {
        int isjmp = (tbl[i].specific & 1);
        int isaddx = (tbl[i].specific & 8);
        int iscjmp = (tbl[i].specific & 16);

        prop[tbl[i].opcode].is_jump = isjmp;
        prop[tbl[i].opcode].is_const_jump = iscjmp;
        prop[tbl[i].opcode].is_addx = isaddx;
        compfunctbl[tbl[i].opcode] = tbl[i].handler;
    }
    for (i = 0; nftbl[i].opcode < 65536; i++)
    {
        nfcompfunctbl[nftbl[i].opcode] = nftbl[i].handler;
        #ifdef NOFLAGS_SUPPORT
        nfcpufunctbl[nftbl[i].opcode] = nfctbl[i].handler;
        #endif
    }

    #ifdef NOFLAGS_SUPPORT
    for (i = 0; nfctbl[i].handler; i++)
    {
        nfcpufunctbl[nfctbl[i].opcode] = nfctbl[i].handler;
    }
    #endif

    for (opcode = 0; opcode < 65536; opcode++)
    {
        compop_func* f;
        compop_func* nff;
        #ifdef NOFLAGS_SUPPORT
        compop_func* nfcf;
        #endif
        int isjmp, isaddx, iscjmp;
        int lvl;

        lvl = (g_curr_conf.cpu_model - 68000) / 10;
        if (lvl > 4)
            lvl--;
        if (table68k[opcode].mnemo == i_ILLG || table68k[opcode].clev > lvl)
            continue;

        if (table68k[opcode].handler != -1)
        {
            f = compfunctbl[table68k[opcode].handler];
            nff = nfcompfunctbl[table68k[opcode].handler];
            #ifdef NOFLAGS_SUPPORT
            nfcf = nfcpufunctbl[table68k[opcode].handler];
            #endif
            isjmp = prop[table68k[opcode].handler].is_jump;
            iscjmp = prop[table68k[opcode].handler].is_const_jump;
            isaddx = prop[table68k[opcode].handler].is_addx;
            prop[opcode].is_jump = isjmp;
            prop[opcode].is_const_jump = iscjmp;
            prop[opcode].is_addx = isaddx;
            compfunctbl[opcode] = f;
            nfcompfunctbl[opcode] = nff;
            #ifdef NOFLAGS_SUPPORT
            Dif(nfcf == op_illg)
            abort();
            nfcpufunctbl[opcode] = nfcf;
            #endif
        }
        prop[opcode].set_flags = table68k[opcode].flagdead;
        prop[opcode].use_flags = table68k[opcode].flaglive;
        /* Unconditional jumps don't evaluate condition codes, so they
           don't actually use any flags themselves */
        if (prop[opcode].is_const_jump)
            prop[opcode].use_flags = 0;
    }
    #ifdef NOFLAGS_SUPPORT
    for (i = 0; nfctbl[i].handler != nullptr; i++)
    {
        if (nfctbl[i].specific)
            nfcpufunctbl[tbl[i].opcode] = nfctbl[i].handler;
    }
    #endif

    count = 0;
    for (opcode = 0; opcode < 65536; opcode++)
    {
        if (compfunctbl[opcode])
            count++;
    }
    if (Logging::JIT_DEBUG)
        Logger::Write(L"JIT: Supposedly %d compileable opcodes!\n", count);

    /* Initialise state */
    alloc_cache();
    create_popalls();
    reset_lists();

    for (i = 0; i < TAGSIZE; i += 2)
    {
        cache_tags[i].handler = (cpuop_func*)popall_execute_normal;
        cache_tags[i + 1].bi = nullptr;
    }
    compemu_reset();

    for (i = 0; i < N_REGS; i++)
    {
        empty_ss.nat[i].holds = -1;
        empty_ss.nat[i].validsize = 0;
        empty_ss.nat[i].dirtysize = 0;
    }
    default_ss = empty_ss;
    #if 0
    default_ss.nat[6].holds = 11;
    default_ss.nat[6].validsize = 4;
    default_ss.nat[5].holds = 12;
    default_ss.nat[5].validsize = 4;
    #endif
}

static void flush_icache_hard(uint ptr, int n)
{
    blockinfo* bi;

    hard_flush_count++;
    #if 0
    Logger::Write(L"JIT: Flush Icache_hard(%d/%x/%p), %u instruction bytes\n",
        n, regs.pc, regs.pc_p, current_compile_p - compiled_code);
    #endif
    bi = active;
    while (bi)
    {
        cache_tags[cacheline(bi->pc_p)].handler = (cpuop_func*)popall_execute_normal;
        cache_tags[cacheline(bi->pc_p) + 1].bi = nullptr;
        bi = bi->next;
    }
    bi = dormant;
    while (bi)
    {
        cache_tags[cacheline(bi->pc_p)].handler = (cpuop_func*)popall_execute_normal;
        cache_tags[cacheline(bi->pc_p) + 1].bi = nullptr;
        bi = bi->next;
    }

    reset_lists();
    if (!compiled_code)
        return;
    current_compile_p = compiled_code;
    set_special(0); /* To get out of compiled code */
}

/* "Soft flushing" --- instead of actually throwing everything away,
   we simply mark everything as "needs to be checked".
 */

void flush_icache(uae_ptr ptr, int n)
{
    blockinfo* bi;
    blockinfo* bi2 = nullptr;

    if (g_curr_conf.jit_hard_flush)
    {
        flush_icache_hard(ptr, n);
        return;
    }
    soft_flush_count++;
    if (!active)
        return;

    bi = active;
    while (bi)
    {
        uint cl = cacheline(bi->pc_p);
        if (!bi->handler)
        {
            /* invalidated block */
            if (bi == cache_tags[cl + 1].bi)
                cache_tags[cl].handler = (cpuop_func*)popall_execute_normal;
            bi->handler_to_use = (cpuop_func*)popall_execute_normal;
            set_dhtu(bi, bi->direct_pen);
        }
        else
        {
            if (bi == cache_tags[cl + 1].bi)
                cache_tags[cl].handler = (cpuop_func*)popall_check_checksum;
            bi->handler_to_use = (cpuop_func*)popall_check_checksum;
            set_dhtu(bi, bi->direct_pcc);
        }
        bi2 = bi;
        bi = bi->next;
    }
    /* bi2 is now the last entry in the active list */
    bi2->next = dormant;
    if (dormant)
        dormant->prev_p = &(bi2->next);

    dormant = active;
    active->prev_p = &dormant;
    active = nullptr;
}

static void catastrophe()
{
    Logger::JitAbort(L"catastprophe");
}

int failure;

void compile_block(cpu_history* pc_hist, int blocklen, int totcycles)
{
    if (letit && compiled_code && g_curr_conf.cpu_model >= 68020)
    {
        /* OK, here we need to 'compile' a block */
        int i;
        int r;
        int was_comp = 0;
        byte liveflags[MAXRUN + 1];
        uint max_pcp = (uint)pc_hist[0].location;
        uint min_pcp = max_pcp;
        uint cl = cacheline(pc_hist[0].location);
        void* specflags = (void*)&regs.spcflags;
        blockinfo* bi = nullptr;
        blockinfo* bi2;
        int extra_len = 0;

        compile_count++;
        if (current_compile_p >= max_compile_start)
            flush_icache_hard(0, 3);

        alloc_blockinfos();

        bi = get_blockinfo_addr_new(pc_hist[0].location, 0);
        bi2 = get_blockinfo(cl);

        optlev = bi->optlevel;
        if (bi->handler)
        {
            Dif(bi != bi2)
            {
                /* I don't think it can happen anymore. Shouldn't, in
                   any case. So let's make sure... */
                Logger::JitAbort(L"JIT: WOOOWOO count=%d, ol=%d %p %p\n",
                    bi->count, bi->optlevel, bi->handler_to_use,
                    cache_tags[cl].handler);
            }

            Dif(bi->count != -1 && bi->status != BI_TARGETTED)
            {
                /* What the heck? We are not supposed to be here! */
                Logger::JitAbort(L"BI_TARGETTED");
            }
        }
        if (bi->count == -1)
        {
            optlev++;
            while (!g_curr_conf.jit_optcount[optlev])
                optlev++;
            bi->count = g_curr_conf.jit_optcount[optlev] - 1;
        }
        current_block_pc_p = (uint)pc_hist[0].location;

        remove_deps(bi); /* We are about to create new code */
        bi->optlevel = optlev;
        bi->pc_p = (byte*)pc_hist[0].location;

        liveflags[blocklen] = 0x1f; /* All flags needed afterwards */
        i = blocklen;
        while (i--)
        {
            ushort* currpcp = pc_hist[i].location;
            int op = cft_map(*currpcp);

            if ((uint)currpcp < min_pcp)
                min_pcp = (uint)currpcp;
            if ((uint)currpcp > max_pcp)
                max_pcp = (uint)currpcp;

            if (g_curr_conf.jit_no_flags)
            {
                liveflags[i] = ((liveflags[i + 1] &
                        (~prop[op].set_flags)) |
                    prop[op].use_flags);
                if (prop[op].is_addx && (liveflags[i + 1] & FLAG_Z) == 0)
                    liveflags[i] &= ~FLAG_Z;
            }
            else
            {
                liveflags[i] = 0x1f;
            }
        }

        bi->needed_flags = liveflags[0];

        /* This is the non-direct handler */
        align_target(32);
        set_target(get_target() + 1);
        align_target(16);
        /* Now aligned at n*32+16 */

        bi->handler =
            bi->handler_to_use = (cpuop_func*)get_target();
        raw_cmp_l_mi((uint) & regs.pc_p, (uint)pc_hist[0].location);
        raw_jnz((uint)popall_cache_miss);
        /* This was 16 bytes on the x86, so now aligned on (n+1)*32 */

        was_comp = 0;

        #if JIT_DEBUG_DEF > 1
        comp_pc_p = (byte*)pc_hist[0].location;
        init_comp();
        match_states(&(bi->env));
        was_comp = 1;
        #endif

        bi->direct_handler = (cpuop_func*)get_target();
        set_dhtu(bi, bi->direct_handler);
        current_block_start_target = (uint)get_target();

        if (bi->count >= 0)   /* Need to generate countdown code */
        {
            raw_mov_l_mi((uint) & regs.pc_p, (uint)pc_hist[0].location);
            raw_sub_l_mi((uint) & (bi->count), 1);
            raw_jl((uint)popall_recompile_block);
        }
        if (optlev == 0)   /* No need to actually translate */
        {   /* Execute normally without keeping stats */
            raw_mov_l_mi((uint) & regs.pc_p, (uint)pc_hist[0].location);
            raw_jmp((uint)popall_exec_nostats);
        }
        else
        {
            reg_alloc_run = 0;
            next_pc_p = 0;
            taken_pc_p = 0;
            branch_cc = 0;

            log_startblock();
            for (i = 0; i < blocklen &&
                get_target_noopt() < max_compile_start; i++)
            {
                cpuop_func** cputbl;
                compop_func** comptbl;
                ushort opcode;

                opcode = cft_map((ushort) * pc_hist[i].location);
                special_mem = pc_hist[i].specmem;
                needed_flags = (liveflags[i + 1] & prop[opcode].set_flags);
                if (!needed_flags && g_curr_conf.jit_no_flags)
                {
                    #ifdef NOFLAGS_SUPPORT
                    cputbl = nfcpufunctbl;
                    #else
                    cputbl = cpufunctbl;
                    #endif
                    comptbl = nfcompfunctbl;
                }
                else
                {
                    cputbl = cpufunctbl;
                    comptbl = compfunctbl;
                }

                if (comptbl[opcode] && optlev > 1)
                {
                    failure = 0;
                    if (!was_comp)
                    {
                        comp_pc_p = (byte*)pc_hist[i].location;
                        init_comp();
                    }
                    was_comp++;

                    comptbl[opcode](opcode);
                    freescratch();
                    if (!(liveflags[i + 1] & FLAG_CZNV))
                    {
                        /* We can forget about flags */
                        dont_care_flags();
                    }
                    #if INDIVIDUAL_INST
                    flush(1);
                    nop();
                    flush(1);
                    was_comp = 0;
                    #endif
                }
                else
                    failure = 1;
                if (failure)
                {
                    if (was_comp)
                    {
                        flush(1);
                        was_comp = 0;
                    }
                    raw_mov_l_ri(REG_PAR1, (uint)opcode);
                    raw_mov_l_ri(REG_PAR2, (uint) & regs);
                    #if USE_NORMAL_CALLING_CONVENTION
                    raw_push_l_r(REG_PAR2);
                    raw_push_l_r(REG_PAR1);
                    #endif
                    raw_mov_l_mi((uint) & regs.pc_p,
                        (uint)pc_hist[i].location);
                    raw_call((uint)cputbl[opcode]);
                    // raw_add_l_mi((uint)&oink,1); // FIXME
                    #if USE_NORMAL_CALLING_CONVENTION
                    raw_inc_sp(8);
                    #endif
                    /*if (needed_flags)
                       raw_mov_l_mi((uint)&foink3,(uint)opcode+65536);
                       else
                       raw_mov_l_mi((uint)&foink3,(uint)opcode);
                     */

                    if (i < blocklen - 1)
                    {
                        sbyte* branchadd;

                        raw_mov_l_rm(0, (uint)specflags);
                        raw_test_l_rr(0, 0);
                        raw_jz_b_oponly();
                        branchadd = (sbyte*)get_target();
                        emit_byte(0);
                        raw_sub_l_mi((uint) & pissoff, scaled_cycles(totcycles));
                        raw_jmp((uint)popall_do_nothing);
                        *branchadd = (uint)get_target() - (uint)branchadd - 1;
                    }
                }
            }
            #if 0 /* This isn't completely kosher yet; It really needs to be
                     be integrated into a general inter-block-dependency scheme */
            if (next_pc_p && taken_pc_p &&
                was_comp && taken_pc_p == current_block_pc_p)
            {
                blockinfo* bi1 = get_blockinfo_addr_new((void*)next_pc_p, 0);
                blockinfo* bi2 = get_blockinfo_addr_new((void*)taken_pc_p, 0);
                byte x = bi1->needed_flags;

                if (x == 0xff || 1)      /* To be on the safe side */
                {
                    ushort* next = (ushort*)next_pc_p;
                    ushort op = cft_map(*next);

                    x = 0x1f;
                    x &= (~prop[op].set_flags);
                    x |= prop[op].use_flags;
                }

                x |= bi2->needed_flags;
                if (!(x & FLAG_CZNV))
                {
                    /* We can forget about flags */
                    dont_care_flags();
                    extra_len += 2;   /* The next instruction now is part of this
                                         block */
                }
            }
            #endif

            if (next_pc_p)   /* A branch was registered */
            {
                uint t1 = next_pc_p;
                uint t2 = taken_pc_p;
                int cc = branch_cc;

                uint* branchadd;
                uint* tba;
                bigstate tmp;
                blockinfo* tbi;

                if (taken_pc_p < next_pc_p)
                {
                    /* backward branch. Optimize for the "taken" case ---
                       which means the raw_jcc should fall through when
                       the 68k branch is taken. */
                    t1 = taken_pc_p;
                    t2 = next_pc_p;
                    cc = branch_cc ^ 1;
                }

                #if JIT_DEBUG_DEF == 0
                flush_keepflags();
                #endif
                tmp = live; /* ouch! This is big... */
                raw_jcc_l_oponly(cc);
                branchadd = (uint*)get_target();
                emit_long(0);
                /* predicted outcome */
                tbi = get_blockinfo_addr_new((void*)t1, 1);
                match_states(&(tbi->env));
                // flush(1); /* Can only get here if was_comp==1 */
                raw_sub_l_mi((uint) & pissoff, scaled_cycles(totcycles));
                raw_jcc_l_oponly(9);
                tba = (uint*)get_target();
                emit_long(get_handler(t1) - ((uint)tba + 4));
                raw_mov_l_mi((uint) & regs.pc_p, t1);
                raw_jmp((uint)popall_do_nothing);
                create_jmpdep(bi, 0, tba, t1);

                align_target(16);
                /* not-predicted outcome */
                *branchadd = (uint)get_target() - ((uint)branchadd + 4);
                live = tmp; /* Ouch again */
                tbi = get_blockinfo_addr_new((void*)t2, 1);
                match_states(&(tbi->env));

                // flush(1); /* Can only get here if was_comp==1 */
                raw_sub_l_mi((uint) & pissoff, scaled_cycles(totcycles));
                raw_jcc_l_oponly(9);
                tba = (uint*)get_target();
                emit_long(get_handler(t2) - ((uint)tba + 4));
                raw_mov_l_mi((uint) & regs.pc_p, t2);
                raw_jmp((uint)popall_do_nothing);
                create_jmpdep(bi, 1, tba, t2);
            }
            else
            {
                if (was_comp)
                {
                    flush(1);
                }

                /* Let's find out where next_handler is... */
                if (was_comp && isinreg(PC_P))
                {
                    int r2;

                    r = live.state[PC_P].realreg;

                    if (r == 0)
                        r2 = 1;
                    else
                        r2 = 0;

                    raw_and_l_ri(r, TAGMASK);
                    raw_mov_l_ri(r2, (uint)popall_do_nothing);
                    raw_sub_l_mi((uint) & pissoff, scaled_cycles(totcycles));
                    raw_cmov_l_rm_indexed(r2, (uint)cache_tags, r, 9);
                    raw_jmp_r(r2);
                }
                else if (was_comp && isconst(PC_P))
                {
                    uint v = live.state[PC_P].val;
                    uint* tba;
                    blockinfo* tbi;

                    tbi = get_blockinfo_addr_new((void*)v, 1);
                    match_states(&(tbi->env));

                    raw_sub_l_mi((uint) & pissoff, scaled_cycles(totcycles));
                    raw_jcc_l_oponly(9);
                    tba = (uint*)get_target();
                    emit_long(get_handler(v) - ((uint)tba + 4));
                    raw_mov_l_mi((uint) & regs.pc_p, v);
                    raw_jmp((uint)popall_do_nothing);
                    create_jmpdep(bi, 0, tba, v);
                }
                else
                {
                    int r2;

                    r = REG_PC_TMP;
                    raw_mov_l_rm(r, (uint) & regs.pc_p);
                    if (r == 0)
                        r2 = 1;
                    else
                        r2 = 0;

                    raw_and_l_ri(r, TAGMASK);
                    raw_mov_l_ri(r2, (uint)popall_do_nothing);
                    raw_sub_l_mi((uint) & pissoff, scaled_cycles(totcycles));
                    raw_cmov_l_rm_indexed(r2, (uint)cache_tags, r, 9);
                    raw_jmp_r(r2);
                }
            }
        }

        if (next_pc_p + extra_len >= max_pcp &&
            next_pc_p + extra_len < max_pcp + LONGEST_68K_INST)
            max_pcp = next_pc_p + extra_len;  /* extra_len covers flags magic */
        else
            max_pcp += LONGEST_68K_INST;
        bi->len = max_pcp - min_pcp;
        bi->min_pcp = min_pcp;

        remove_from_list(bi);

        calc_checksum(bi, &(bi->c1), &(bi->c2));
        add_to_active(bi);

        log_dump();
        align_target(32);
        current_compile_p = get_target();

        raise_in_cl_list(bi);
        bi->nexthandler = current_compile_p;

        /* We will flush soon, anyway, so let's do it now */
        if (current_compile_p >= max_compile_start)
            flush_icache_hard(0, 3);

        do_extra_cycles(totcycles); /* for the compilation time */
    }
}

#endif